INFRA-132 - Newline after "{"
[civicrm-core.git] / CRM / Contact / BAO / Contact.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
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 +--------------------------------------------------------------------+
26*/
27
28/**
29 *
30 * @package CRM
06b69b18 31 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
32 * $Id$
33 *
34 */
35class CRM_Contact_BAO_Contact extends CRM_Contact_DAO_Contact {
36
37 /**
38 * SQL function used to format the phone_numeric field via trigger.
39 * @see self::triggerInfo()
40 *
41 * Note that this is also used by the 4.3 upgrade script.
42 * @see CRM_Upgrade_Incremental_php_FourThree
43 */
44 const DROP_STRIP_FUNCTION_43 = "DROP FUNCTION IF EXISTS civicrm_strip_non_numeric";
45 const CREATE_STRIP_FUNCTION_43 = "
46 CREATE FUNCTION civicrm_strip_non_numeric(input VARCHAR(255) CHARACTER SET utf8)
47 RETURNS VARCHAR(255) CHARACTER SET utf8
48 DETERMINISTIC
49 NO SQL
50 BEGIN
51 DECLARE output VARCHAR(255) CHARACTER SET utf8 DEFAULT '';
52 DECLARE iterator INT DEFAULT 1;
53 WHILE iterator < (LENGTH(input) + 1) DO
54 IF SUBSTRING(input, iterator, 1) IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') THEN
55 SET output = CONCAT(output, SUBSTRING(input, iterator, 1));
56 END IF;
57 SET iterator = iterator + 1;
58 END WHILE;
59 RETURN output;
60 END";
61
62 /**
100fef9d 63 * The types of communication preferences
6a488035
TO
64 *
65 * @var array
66 */
67 static $_commPrefs = array('do_not_phone', 'do_not_email', 'do_not_mail', 'do_not_sms', 'do_not_trade');
68
69 /**
100fef9d 70 * Types of greetings
6a488035
TO
71 *
72 * @var array
73 */
74 static $_greetingTypes = array('addressee', 'email_greeting', 'postal_greeting');
75
76 /**
100fef9d 77 * Static field for all the contact information that we can potentially import
6a488035
TO
78 *
79 * @var array
80 * @static
81 */
82 static $_importableFields = array();
83
84 /**
100fef9d 85 * Static field for all the contact information that we can potentially export
6a488035
TO
86 *
87 * @var array
88 * @static
89 */
90 static $_exportableFields = NULL;
86538308
EM
91
92 /**
100fef9d 93 * Class constructor
86538308 94 *
86538308
EM
95 * @return \CRM_Contact_DAO_Contact
96 */
97 /**
98 *
99 */
00be9182 100 public function __construct() {
6a488035
TO
101 parent::__construct();
102 }
103
104 /**
100fef9d 105 * Takes an associative array and creates a contact object
6a488035
TO
106 *
107 * the function extract all the params it needs to initialize the create a
108 * contact object. the params array could contain additional unused name/value
109 * pairs
110 *
77c5b619
TO
111 * @param array $params
112 * (reference ) an assoc array of name/value pairs.
6a488035 113 *
c490a46a 114 * @return CRM_Contact_BAO_Contact object
6a488035
TO
115 * @static
116 */
00be9182 117 public static function add(&$params) {
6a488035
TO
118 $contact = new CRM_Contact_DAO_Contact();
119
120 if (empty($params)) {
121 return;
122 }
123
124 //fix for validate contact sub type CRM-5143
9bb77b8e
TO
125 if (isset($params['contact_sub_type'])) {
126 if (empty($params['contact_sub_type'])) {
6a488035
TO
127 $params['contact_sub_type'] = 'null';
128 }
129 else {
130 if (!CRM_Contact_BAO_ContactType::isExtendsContactType($params['contact_sub_type'],
131 $params['contact_type'], TRUE
9bb77b8e
TO
132 )
133 ) {
6a488035
TO
134 // we'll need to fix tests to handle this
135 // CRM-7925
136 CRM_Core_Error::fatal(ts('The Contact Sub Type does not match the Contact type for this record'));
137 }
138 if (is_array($params['contact_sub_type'])) {
139 $params['contact_sub_type'] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, $params['contact_sub_type']) . CRM_Core_DAO::VALUE_SEPARATOR;
140 }
141 else {
142 $params['contact_sub_type'] = CRM_Core_DAO::VALUE_SEPARATOR . trim($params['contact_sub_type'], CRM_Core_DAO::VALUE_SEPARATOR) . CRM_Core_DAO::VALUE_SEPARATOR;
143 }
144 }
145 }
146 else {
147 // reset the value
148 // CRM-101XX
149 $params['contact_sub_type'] = 'null';
150 }
151
152 //fixed contact source
153 if (isset($params['contact_source'])) {
154 $params['source'] = $params['contact_source'];
155 }
156
157 //fix for preferred communication method
158 $prefComm = CRM_Utils_Array::value('preferred_communication_method', $params);
159 if ($prefComm && is_array($prefComm)) {
160 unset($params['preferred_communication_method']);
161 $newPref = array();
162
163 foreach ($prefComm as $k => $v) {
164 if ($v) {
165 $newPref[$k] = $v;
166 }
167 }
168
169 $prefComm = $newPref;
170 if (is_array($prefComm) && !empty($prefComm)) {
171 $prefComm = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, array_keys($prefComm)) . CRM_Core_DAO::VALUE_SEPARATOR;
172 $contact->preferred_communication_method = $prefComm;
173 }
174 else {
175 $contact->preferred_communication_method = '';
176 }
177 }
178
179 $allNull = $contact->copyValues($params);
180
181 $contact->id = CRM_Utils_Array::value('contact_id', $params);
182
183 if ($contact->contact_type == 'Individual') {
184 $allNull = FALSE;
185
186 //format individual fields
187 CRM_Contact_BAO_Individual::format($params, $contact);
188 }
189 elseif ($contact->contact_type == 'Household') {
190 if (isset($params['household_name'])) {
191 $allNull = FALSE;
192 $contact->display_name = $contact->sort_name = CRM_Utils_Array::value('household_name', $params, '');
193 }
194 }
195 elseif ($contact->contact_type == 'Organization') {
196 if (isset($params['organization_name'])) {
197 $allNull = FALSE;
198 $contact->display_name = $contact->sort_name = CRM_Utils_Array::value('organization_name', $params, '');
199 }
200 }
201
202 // privacy block
203 $privacy = CRM_Utils_Array::value('privacy', $params);
204 if ($privacy &&
205 is_array($privacy) &&
206 !empty($privacy)
207 ) {
208 $allNull = FALSE;
209 foreach (self::$_commPrefs as $name) {
210 $contact->$name = CRM_Utils_Array::value($name, $privacy, FALSE);
211 }
212 }
213
214 // since hash was required, make sure we have a 0 value for it, CRM-1063
215 // fixed in 1.5 by making hash optional
216 // only do this in create mode, not update
217 if ((!array_key_exists('hash', $contact) || !$contact->hash) && !$contact->id) {
218 $allNull = FALSE;
219 $contact->hash = md5(uniqid(rand(), TRUE));
220 }
221
222 // Even if we don't need $employerId, it's important to call getFieldValue() before
223 // the contact is saved because we want the existing value to be cached.
224 // createCurrentEmployerRelationship() needs the old value not the updated one. CRM-10788
225 $employerId = empty($contact->id) ? NULL : CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $contact->id, 'employer_id');
226
227 if (!$allNull) {
228 $contact->save();
229
230 CRM_Core_BAO_Log::register($contact->id,
231 'civicrm_contact',
232 $contact->id
233 );
234 }
235
fad0497c 236 if ($contact->contact_type == 'Individual' && (isset($params['current_employer']) || isset($params['employer_id']))) {
6a488035 237 // create current employer
f06fc7d2 238 $newEmployer = !empty($params['employer_id']) ? $params['employer_id'] : CRM_Utils_Array::value('current_employer', $params);
239
240 $newContact = FALSE;
241 if (empty($params['contact_id'])) {
242 $newContact = TRUE;
6a488035 243 }
fad0497c 244 if ($newEmployer) {
6eae3162 245 CRM_Contact_BAO_Contact_Utils::createCurrentEmployerRelationship($contact->id, $newEmployer, $employerId, $newContact);
6a488035
TO
246 }
247 else {
248 //unset if employer id exits
249 if ($employerId) {
250 CRM_Contact_BAO_Contact_Utils::clearCurrentEmployer($contact->id, $employerId);
251 }
252 }
253 }
254
255 //update cached employee name
256 if ($contact->contact_type == 'Organization') {
257 CRM_Contact_BAO_Contact_Utils::updateCurrentEmployer($contact->id);
258 }
259
260 return $contact;
261 }
262
263 /**
100fef9d 264 * Create contact
6a488035
TO
265 * takes an associative array and creates a contact object and all the associated
266 * derived objects (i.e. individual, location, email, phone etc)
267 *
268 * This function is invoked from within the web form layer and also from the api layer
269 *
77c5b619
TO
270 * @param array $params
271 * (reference ) an assoc array of name/value pairs.
272 * @param bool $fixAddress
273 * If we need to fix address.
274 * @param bool $invokeHooks
275 * If we need to invoke hooks.
6a488035 276 *
d9f93da9
EM
277 * @param bool $skipDelete
278 *
279 * @throws Exception
c490a46a 280 * @return CRM_Contact_BAO_Contact object
6a488035
TO
281 * @static
282 */
00be9182 283 public static function &create(&$params, $fixAddress = TRUE, $invokeHooks = TRUE, $skipDelete = FALSE) {
9bb77b8e
TO
284 $contact = NULL;
285 if (empty($params['contact_type']) && empty($params['contact_id'])) {
286 return $contact;
287 }
6a488035 288
9bb77b8e
TO
289 $isEdit = TRUE;
290 if ($invokeHooks) {
291 if (!empty($params['contact_id'])) {
292 CRM_Utils_Hook::pre('edit', $params['contact_type'], $params['contact_id'], $params);
293 }
294 else {
295 CRM_Utils_Hook::pre('create', $params['contact_type'], NULL, $params);
296 $isEdit = FALSE;
6a488035 297 }
9bb77b8e 298 }
6a488035 299
9bb77b8e 300 $config = CRM_Core_Config::singleton();
6a488035 301
9bb77b8e
TO
302 // CRM-6942: set preferred language to the current language if it’s unset (and we’re creating a contact)
303 if (empty($params['contact_id']) && empty($params['preferred_language'])) {
304 $params['preferred_language'] = $config->lcMessages;
305 }
6a488035 306
9bb77b8e
TO
307 // CRM-9739: set greeting & addressee if unset and we’re creating a contact
308 if (empty($params['contact_id'])) {
309 foreach (self::$_greetingTypes as $greeting) {
310 if (empty($params[$greeting . '_id'])) {
311 if ($defaultGreetingTypeId =
312 CRM_Contact_BAO_Contact_Utils::defaultGreeting($params['contact_type'], $greeting)
313 ) {
314 $params[$greeting . '_id'] = $defaultGreetingTypeId;
6a488035
TO
315 }
316 }
317 }
9bb77b8e 318 }
6a488035 319
9bb77b8e 320 $transaction = new CRM_Core_Transaction();
6a488035 321
9bb77b8e
TO
322 $contact = self::add($params);
323 if (!$contact) {
324 // not dying here is stupid, since we get into wierd situation and into a bug that
325 // is impossible to figure out for the user or for us
326 // CRM-7925
327 CRM_Core_Error::fatal();
328 }
6a488035 329
9bb77b8e 330 $params['contact_id'] = $contact->id;
6a488035 331
9bb77b8e
TO
332 if (CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MULTISITE_PREFERENCES_NAME, 'is_enabled')) {
333 // Enabling multisite causes the contact to be added to the domain group
334 $domainGroupID = CRM_Core_BAO_Domain::getGroupId();
335 if (!empty($domainGroupID)) {
336 if (!empty($params['group']) && is_array($params['group'])) {
337 $params['group'][$domainGroupID] = 1;
6a488035 338 }
9bb77b8e
TO
339 else {
340 $params['group'] = array($domainGroupID => 1);
6a488035
TO
341 }
342 }
9bb77b8e 343 }
6a488035 344
9bb77b8e
TO
345 if (array_key_exists('group', $params)) {
346 $contactIds = array($params['contact_id']);
347 foreach ($params['group'] as $groupId => $flag) {
348 if ($flag == 1) {
349 CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $groupId);
350 }
351 elseif ($flag == -1) {
352 CRM_Contact_BAO_GroupContact::removeContactsFromGroup($contactIds, $groupId);
353 }
6a488035 354 }
9bb77b8e 355 }
6a488035 356
9bb77b8e
TO
357 //add location Block data
358 $blocks = CRM_Core_BAO_Location::create($params, $fixAddress);
359 foreach ($blocks as $name => $value) {
360 $contact->$name = $value;
361 }
6a488035 362
9bb77b8e
TO
363 //add website
364 CRM_Core_BAO_Website::create($params['website'], $contact->id, $skipDelete);
6a488035 365
9bb77b8e
TO
366 //get userID from session
367 $session = CRM_Core_Session::singleton();
368 $userID = $session->get('userID');
369 // add notes
370 if (!empty($params['note'])) {
371 if (is_array($params['note'])) {
372 foreach ($params['note'] as $note) {
6a488035
TO
373 $contactId = $contact->id;
374 if (isset($note['contact_id'])) {
375 $contactId = $note['contact_id'];
376 }
377 //if logged in user, overwrite contactId
378 if ($userID) {
379 $contactId = $userID;
380 }
381
382 $noteParams = array(
383 'entity_id' => $contact->id,
384 'entity_table' => 'civicrm_contact',
9bb77b8e
TO
385 'note' => $note['note'],
386 'subject' => CRM_Utils_Array::value('subject', $note),
6a488035
TO
387 'contact_id' => $contactId,
388 );
389 CRM_Core_BAO_Note::add($noteParams, CRM_Core_DAO::$_nullArray);
390 }
391 }
9bb77b8e
TO
392 else {
393 $contactId = $contact->id;
394 if (isset($note['contact_id'])) {
395 $contactId = $note['contact_id'];
396 }
397 //if logged in user, overwrite contactId
398 if ($userID) {
399 $contactId = $userID;
400 }
6a488035 401
9bb77b8e
TO
402 $noteParams = array(
403 'entity_id' => $contact->id,
404 'entity_table' => 'civicrm_contact',
405 'note' => $params['note'],
406 'subject' => CRM_Utils_Array::value('subject', $params),
407 'contact_id' => $contactId,
6a488035 408 );
9bb77b8e 409 CRM_Core_BAO_Note::add($noteParams, CRM_Core_DAO::$_nullArray);
6a488035 410 }
9bb77b8e 411 }
6a488035 412
6a488035 413
9bb77b8e
TO
414 // update the UF user_unique_id if that has changed
415 CRM_Core_BAO_UFMatch::updateUFName($contact->id);
416
417 if (!empty($params['custom']) &&
418 is_array($params['custom'])
419 ) {
420 CRM_Core_BAO_CustomValueTable::store($params['custom'], 'civicrm_contact', $contact->id);
421 }
422
423 // make a civicrm_subscription_history entry only on contact create (CRM-777)
424 if (empty($params['contact_id'])) {
425 $subscriptionParams = array(
426 'contact_id' => $contact->id,
427 'status' => 'Added',
428 'method' => 'Admin',
6a488035 429 );
9bb77b8e
TO
430 CRM_Contact_BAO_SubscriptionHistory::create($subscriptionParams);
431 }
6a488035 432
9bb77b8e 433 $transaction->commit();
6a488035 434
9bb77b8e
TO
435 // CRM-6367: fetch the right label for contact type’s display
436 $contact->contact_type_display = CRM_Core_DAO::getFieldValue(
437 'CRM_Contact_DAO_ContactType',
438 $contact->contact_type,
439 'label',
440 'name'
441 );
442
443 if (!$config->doNotResetCache) {
444 // Note: doNotResetCache flag is currently set by import contact process and merging,
445 // since resetting and
446 // rebuilding cache could be expensive (for many contacts). We might come out with better
447 // approach in future.
448 CRM_Contact_BAO_Contact_Utils::clearContactCaches($contact->id);
449 }
450
451 if ($invokeHooks) {
452 if ($isEdit) {
453 CRM_Utils_Hook::post('edit', $params['contact_type'], $contact->id, $contact);
454 }
455 else {
456 CRM_Utils_Hook::post('create', $params['contact_type'], $contact->id, $contact);
6a488035 457 }
9bb77b8e 458 }
6a488035 459
9bb77b8e
TO
460 // process greetings CRM-4575, cache greetings
461 self::processGreetings($contact);
6a488035 462
9bb77b8e 463 return $contact;
6a488035
TO
464 }
465
466 /**
467 * Get the display name and image of a contact
468 *
77c5b619
TO
469 * @param int $id
470 * The contactId.
6a488035 471 *
d9f93da9
EM
472 * @param bool $type
473 *
6a488035 474 * @return array the displayName and contactImage for this contact
6a488035
TO
475 * @static
476 */
00be9182 477 public static function getDisplayAndImage($id, $type = FALSE) {
95fec3ce 478 //CRM-14276 added the * on the civicrm_contact table so that we have all the contact info available
250b3b1f 479 $sql = "
95fec3ce 480SELECT civicrm_contact.*,
6a488035
TO
481 civicrm_email.email as email
482FROM civicrm_contact
483LEFT JOIN civicrm_email ON civicrm_email.contact_id = civicrm_contact.id
484 AND civicrm_email.is_primary = 1
485WHERE civicrm_contact.id = " . CRM_Utils_Type::escape($id, 'Integer');
486 $dao = new CRM_Core_DAO();
487 $dao->query($sql);
488 if ($dao->fetch()) {
489 $image = CRM_Contact_BAO_Contact_Utils::getImage($dao->contact_sub_type ?
250b3b1f 490 $dao->contact_sub_type : $dao->contact_type, FALSE, $id
6a488035
TO
491 );
492 $imageUrl = CRM_Contact_BAO_Contact_Utils::getImage($dao->contact_sub_type ?
250b3b1f 493 $dao->contact_sub_type : $dao->contact_type, TRUE, $id
6a488035
TO
494 );
495
496 // use email if display_name is empty
497 if (empty($dao->display_name)) {
250b3b1f 498 $displayName = $dao->email;
499 }
500 else {
501 $displayName = $dao->display_name;
502 }
503
504 CRM_Utils_Hook::alterDisplayName($displayName, $id, $dao);
95fec3ce 505
6a488035 506 return $type ? array(
967fddb5 507 $displayName,
250b3b1f 508 $image,
509 $dao->contact_type,
510 $dao->contact_sub_type,
511 $imageUrl,
967fddb5 512 ) : array($displayName, $image, $imageUrl);
6a488035
TO
513 }
514 return NULL;
515 }
516
688ad538 517 /**
77c5b619
TO
518 * @param array $crudLinkSpec
519 * With keys:.
688ad538
TO
520 * - action: int, CRM_Core_Action::UPDATE or CRM_Core_Action::VIEW [default: VIEW]
521 * - entity_table: string, eg "civicrm_contact"
522 * - entity_id: int
523 * @return array|NULL NULL if unavailable, or an array. array has keys:
524 * - path: string
525 * - query: string
526 * - title: string
527 * @see CRM_Utils_System::createDefaultCrudLink
528 */
529 public function createDefaultCrudLink($crudLinkSpec) {
530 switch ($crudLinkSpec['action']) {
531 case CRM_Core_Action::VIEW:
532 return array(
533 'title' => $this->display_name,
534 'path' => 'civicrm/contact/view',
535 'query' => array(
536 'reset' => 1,
537 'cid' => $this->id,
538 ),
539 );
540 case CRM_Core_Action::UPDATE:
541 return array(
542 'title' => $this->display_name,
543 'path' => 'civicrm/contact/add',
544 'query' => array(
545 'reset' => 1,
546 'action' => 'update',
547 'cid' => $this->id,
548 ),
549 );
550 }
551 return NULL;
552 }
553
6a488035 554 /**
6a488035
TO
555 * Get the values for pseudoconstants for name->value and reverse.
556 *
77c5b619
TO
557 * @param array $defaults
558 * (reference) the default values, some of which need to be resolved.
559 * @param bool $reverse
560 * True if we want to resolve the values in the reverse direction (value -> name).
6a488035 561 *
355ba699 562 * @return void
6a488035
TO
563 * @static
564 */
00be9182 565 public static function resolveDefaults(&$defaults, $reverse = FALSE) {
6a488035 566 // hack for birth_date
a7488080 567 if (!empty($defaults['birth_date'])) {
6a488035
TO
568 if (is_array($defaults['birth_date'])) {
569 $defaults['birth_date'] = CRM_Utils_Date::format($defaults['birth_date'], '-');
570 }
571 }
572
e6c4755b
CW
573 CRM_Utils_Array::lookupValue($defaults, 'prefix', CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id'), $reverse);
574 CRM_Utils_Array::lookupValue($defaults, 'suffix', CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id'), $reverse);
26cf88b5 575 CRM_Utils_Array::lookupValue($defaults, 'gender', CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id'), $reverse);
aa62b355 576 CRM_Utils_Array::lookupValue($defaults, 'communication_style', CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'communication_style_id'), $reverse);
6a488035
TO
577
578 //lookup value of email/postal greeting, addressee, CRM-4575
579 foreach (self::$_greetingTypes as $greeting) {
9bb77b8e
TO
580 $filterCondition = array(
581 'contact_type' => CRM_Utils_Array::value('contact_type', $defaults),
6a488035
TO
582 'greeting_type' => $greeting,
583 );
584 CRM_Utils_Array::lookupValue($defaults, $greeting,
585 CRM_Core_PseudoConstant::greeting($filterCondition), $reverse
586 );
587 }
588
589 $blocks = array('address', 'im', 'phone');
590 foreach ($blocks as $name) {
591 if (!array_key_exists($name, $defaults) || !is_array($defaults[$name])) {
592 continue;
593 }
594 foreach ($defaults[$name] as $count => & $values) {
595
596 //get location type id.
b2b0530a 597 CRM_Utils_Array::lookupValue($values, 'location_type', CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'), $reverse);
6a488035
TO
598
599 if ($name == 'address') {
600 // FIXME: lookupValue doesn't work for vcard_name
a7488080 601 if (!empty($values['location_type_id'])) {
8f785c9e 602 $vcardNames = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', array('labelColumn' => 'vcard_name'));
6a488035
TO
603 $values['vcard_name'] = $vcardNames[$values['location_type_id']];
604 }
605
606 if (!CRM_Utils_Array::lookupValue($values,
607 'country',
608 CRM_Core_PseudoConstant::country(),
609 $reverse
610 ) &&
611 $reverse
612 ) {
613 CRM_Utils_Array::lookupValue($values,
614 'country',
615 CRM_Core_PseudoConstant::countryIsoCode(),
616 $reverse
617 );
618 }
619
620 // CRM-7597
621 // if we find a country id above, we need to restrict it to that country
622 // rather than the list of all countries
623
624 if (!empty($values['country_id'])) {
625 $stateProvinceList = CRM_Core_PseudoConstant::stateProvinceForCountry($values['country_id']);
626 }
627 else {
628 $stateProvinceList = CRM_Core_PseudoConstant::stateProvince();
629 }
630 if (!CRM_Utils_Array::lookupValue($values,
631 'state_province',
632 $stateProvinceList,
633 $reverse
634 ) &&
635 $reverse
636 ) {
637
638 if (!empty($values['country_id'])) {
639 $stateProvinceList = CRM_Core_PseudoConstant::stateProvinceForCountry($values['country_id'], 'abbreviation');
640 }
641 else {
642 $stateProvinceList = CRM_Core_PseudoConstant::stateProvinceAbbreviation();
643 }
644 CRM_Utils_Array::lookupValue($values,
645 'state_province',
646 $stateProvinceList,
647 $reverse
648 );
649 }
650
651 if (!empty($values['state_province_id'])) {
652 $countyList = CRM_Core_PseudoConstant::countyForState($values['state_province_id']);
653 }
654 else {
655 $countyList = CRM_Core_PseudoConstant::county();
656 }
657 CRM_Utils_Array::lookupValue($values,
658 'county',
659 $countyList,
660 $reverse
661 );
662 }
663
664 if ($name == 'im') {
665 CRM_Utils_Array::lookupValue($values,
666 'provider',
e7e657f0 667 CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'),
6a488035
TO
668 $reverse
669 );
670 }
671
672 if ($name == 'phone') {
673 CRM_Utils_Array::lookupValue($values,
674 'phone_type',
b4f964d9 675 CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id'),
6a488035
TO
676 $reverse
677 );
678 }
679
680 //kill the reference.
681 unset($values);
682 }
683 }
684 }
685
686 /**
c490a46a 687 * Fetch object based on array of properties
6a488035 688 *
77c5b619
TO
689 * @param array $params
690 * (reference ) an assoc array of name/value pairs.
691 * @param array $defaults
692 * (reference ) an assoc array to hold the name / value pairs.
6a488035 693 * in a hierarchical manner
77c5b619
TO
694 * @param bool $microformat
695 * For location in microformat.
6a488035 696 *
c490a46a 697 * @return CRM_Contact_BAO_Contact object
6a488035
TO
698 * @static
699 */
00be9182 700 public static function &retrieve(&$params, &$defaults, $microformat = FALSE) {
6a488035
TO
701 if (array_key_exists('contact_id', $params)) {
702 $params['id'] = $params['contact_id'];
703 }
704 elseif (array_key_exists('id', $params)) {
705 $params['contact_id'] = $params['id'];
706 }
707
708 $contact = self::getValues($params, $defaults);
709
710 unset($params['id']);
711
712 //get the block information for this contact
713 $entityBlock = array('contact_id' => $params['contact_id']);
9bb77b8e
TO
714 $blocks = CRM_Core_BAO_Location::getValues($entityBlock, $microformat);
715 $defaults = array_merge($defaults, $blocks);
716 foreach ($blocks as $block => $value) {
717 $contact->$block = $value;
718 }
6a488035
TO
719
720 if (!isset($params['noNotes'])) {
721 $contact->notes = CRM_Core_BAO_Note::getValues($params, $defaults);
722 }
723
724 if (!isset($params['noRelationships'])) {
725 $contact->relationship = CRM_Contact_BAO_Relationship::getValues($params, $defaults);
726 }
727
728 if (!isset($params['noGroups'])) {
729 $contact->groupContact = CRM_Contact_BAO_GroupContact::getValues($params, $defaults);
730 }
731
732 if (!isset($params['noWebsite'])) {
733 $contact->website = CRM_Core_BAO_Website::getValues($params, $defaults);
734 }
735
736 return $contact;
737 }
738
739 /**
100fef9d 740 * Get the display name of a contact
6a488035 741 *
77c5b619
TO
742 * @param int $id
743 * Id of the contact.
6a488035
TO
744 *
745 * @return null|string display name of the contact if found
746 * @static
6a488035 747 */
00be9182 748 public static function displayName($id) {
6a488035
TO
749 $displayName = NULL;
750 if ($id) {
751 $displayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $id, 'display_name');
752 }
753
754 return $displayName;
755 }
756
757 /**
758 * Delete a contact and all its associated records
759 *
77c5b619
TO
760 * @param int $id
761 * Id of the contact to delete.
762 * @param bool $restore
763 * Whether to actually restore, not delete.
764 * @param bool $skipUndelete
765 * Whether to force contact delete or not.
6a488035
TO
766 *
767 * @return boolean true if contact deleted, false otherwise
6a488035
TO
768 * @static
769 */
00be9182 770 public static function deleteContact($id, $restore = FALSE, $skipUndelete = FALSE) {
6a488035
TO
771
772 if (!$id) {
773 return FALSE;
774 }
775
776 // make sure we have edit permission for this contact
777 // before we delete
778 if (($skipUndelete && !CRM_Core_Permission::check('delete contacts')) ||
779 ($restore && !CRM_Core_Permission::check('access deleted contacts'))
780 ) {
781 return FALSE;
782 }
2efcf0c2 783
f182074e
PN
784 // CRM-12929
785 // Restrict contact to be delete if contact has financial trxns
786 $error = NULL;
787 if ($skipUndelete && CRM_Financial_BAO_FinancialItem::checkContactPresent(array($id), $error)) {
788 return FALSE;
789 }
6a488035
TO
790
791 // make sure this contact_id does not have any membership types
792 $membershipTypeID = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
793 $id,
794 'id',
795 'member_of_contact_id'
796 );
797 if ($membershipTypeID) {
798 return FALSE;
799 }
800
801 $contact = new CRM_Contact_DAO_Contact();
802 $contact->id = $id;
803 if (!$contact->find(TRUE)) {
804 return FALSE;
805 }
806
807 $contactType = $contact->contact_type;
808 $action = ($restore) ? 'restore' : 'delete';
809
810 CRM_Utils_Hook::pre($action, $contactType, $id, CRM_Core_DAO::$_nullArray);
811
812 if ($restore) {
813 self::contactTrashRestore($contact, TRUE);
814 CRM_Utils_Hook::post($action, $contactType, $contact->id, $contact);
815 return TRUE;
816 }
817
818
819 // currently we only clear employer cache.
820 // we are not deleting inherited membership if any.
821 if ($contact->contact_type == 'Organization') {
822 CRM_Contact_BAO_Contact_Utils::clearAllEmployee($id);
823 }
824
825 // start a new transaction
826 $transaction = new CRM_Core_Transaction();
827
828 if ($skipUndelete or !CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'contact_undelete', NULL)) {
829
830 //delete billing address if exists.
831 CRM_Contribute_BAO_Contribution::deleteAddress(NULL, $id);
832
833 // delete the log entries since we dont have triggers enabled as yet
834 $logDAO = new CRM_Core_DAO_Log();
835 $logDAO->entity_table = 'civicrm_contact';
836 $logDAO->entity_id = $id;
837 $logDAO->delete();
1071730c 838
c3d24ba7
PN
839 // delete contact participants CRM-12155
840 CRM_Event_BAO_Participant::deleteContactParticipant($id);
6a488035 841
c3d24ba7
PN
842 // delete contact contributions CRM-12155
843 CRM_Contribute_BAO_Contribution::deleteContactContribution($id);
1071730c 844
6a488035
TO
845 // do activity cleanup, CRM-5604
846 CRM_Activity_BAO_Activity::cleanupActivity($id);
847
848 // delete all notes related to contact
849 CRM_Core_BAO_Note::cleanContactNotes($id);
850
55033e60
PJ
851 // delete cases related to contact
852 $contactCases = CRM_Case_BAO_Case::retrieveCaseIdsByContactId($id);
853 if (!empty($contactCases)) {
d3b60a94 854 foreach ($contactCases as $caseId) {
55033e60
PJ
855 //check if case is associate with other contact or not.
856 $caseContactId = CRM_Case_BAO_Case::getCaseClients($caseId);
d3b60a94 857 if (count($caseContactId) <= 1) {
55033e60
PJ
858 CRM_Case_BAO_Case::deleteCase($caseId);
859 }
860 }
861 }
862
6a488035
TO
863 $contact->delete();
864 }
865 else {
866 self::contactTrashRestore($contact);
867 }
868
869 //delete the contact id from recently view
870 CRM_Utils_Recent::delContact($id);
871
2c6bbd06
CW
872 // Update the group contact cache
873 if ($restore) {
874 CRM_Contact_BAO_GroupContactCache::remove();
875 }
876 else {
877 CRM_Contact_BAO_GroupContactCache::removeContact($id);
878 }
6a488035
TO
879
880 // delete any dupe cache entry
881 CRM_Core_BAO_PrevNextCache::deleteItem($id);
882
883 $transaction->commit();
884
885 CRM_Utils_Hook::post('delete', $contactType, $contact->id, $contact);
886
887 // also reset the DB_DO global array so we can reuse the memory
888 // http://issues.civicrm.org/jira/browse/CRM-4387
889 CRM_Core_DAO::freeResult();
890
891 return TRUE;
892 }
893
894 /**
100fef9d 895 * Delete the image of a contact
6a488035 896 *
77c5b619
TO
897 * @param int $id
898 * Id of the contact.
6a488035
TO
899 *
900 * @return boolean true if contact image is deleted
901 */
902 public static function deleteContactImage($id) {
903 if (!$id) {
904 return FALSE;
905 }
906 $query = "
907UPDATE civicrm_contact
908SET image_URL=NULL
909WHERE id={$id}; ";
910 CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
911 return TRUE;
912 }
913
914 /**
c490a46a 915 * Return relative path
6a488035
TO
916 * @todo make this a method of $config->userSystem (i.e. UF classes) rather than a static function
917 *
77c5b619
TO
918 * @param string $absolutePath
919 * Absolute path.
6a488035
TO
920 *
921 * @return String $relativePath Relative url of uploaded image
922 */
923 public static function getRelativePath($absolutePath) {
924 $relativePath = NULL;
925 $config = CRM_Core_Config::singleton();
926 if ($config->userFramework == 'Joomla') {
927 $userFrameworkBaseURL = trim(str_replace('/administrator/', '', $config->userFrameworkBaseURL));
928 $customFileUploadDirectory = strstr(str_replace('\\', '/', $absolutePath), '/media');
929 $relativePath = $userFrameworkBaseURL . $customFileUploadDirectory;
930 }
931 elseif ($config->userSystem->is_drupal == '1') {
932 //ideally we would do a bigger re-factoring & move the getRelativePath onto the UF class
933 $rootPath = $config->userSystem->cmsRootPath();
934 $baseUrl = $config->userFrameworkBaseURL;
935
936 //format url for language negotiation, CRM-7135
937 $baseUrl = CRM_Utils_System::languageNegotiationURL($baseUrl, FALSE, TRUE);
938
939 $relativePath = str_replace("{$rootPath}/",
940 $baseUrl,
941 str_replace('\\', '/', $absolutePath)
942 );
9bb77b8e
TO
943 }
944 else if ($config->userFramework == 'WordPress') {
945 $userFrameworkBaseURL = trim(str_replace('/wp-admin/', '', $config->userFrameworkBaseURL));
946 $customFileUploadDirectory = strstr(str_replace('\\', '/', $absolutePath), '/wp-content/');
947 $relativePath = $userFrameworkBaseURL . $customFileUploadDirectory;
6a488035
TO
948 }
949
950 return $relativePath;
951 }
952
953 /**
100fef9d 954 * Return proportional height and width of the image
6a488035 955 *
77c5b619
TO
956 * @param Integer $imageWidth
957 * Width of image.
6a488035 958 *
77c5b619
TO
959 * @param Integer $imageHeight
960 * Height of image.
6a488035
TO
961 *
962 * @return Array thumb dimension of image
963 */
964 public static function getThumbSize($imageWidth, $imageHeight) {
965 $thumbWidth = 100;
966 if ($imageWidth && $imageHeight) {
967 $imageRatio = $imageWidth / $imageHeight;
968 }
969 else {
970 $imageRatio = 1;
971 }
972 if ($imageRatio > 1) {
973 $imageThumbWidth = $thumbWidth;
974 $imageThumbHeight = round($thumbWidth / $imageRatio);
975 }
976 else {
977 $imageThumbHeight = $thumbWidth;
978 $imageThumbWidth = round($thumbWidth * $imageRatio);
979 }
980
981 return array($imageThumbWidth, $imageThumbHeight);
982 }
983
984 /**
c490a46a 985 * Validate type of contact image
6a488035 986 *
c490a46a 987 * @param array $params
77c5b619
TO
988 * @param String $imageIndex
989 * Index of image field.
990 * @param String $statusMsg
991 * Status message to be set after operation.
992 * @param string $opType
993 * Type of operation like fatal, bounce etc.
6a488035
TO
994 *
995 * @return boolean true if valid image extension
996 */
9bb77b8e
TO
997 public static function processImageParams(
998 &$params,
6a488035 999 $imageIndex = 'image_URL',
9bb77b8e
TO
1000 $statusMsg = NULL,
1001 $opType = 'status'
6a488035
TO
1002 ) {
1003 $mimeType = array(
1004 'image/jpeg',
1005 'image/jpg',
1006 'image/png',
1007 'image/bmp',
1008 'image/p-jpeg',
1009 'image/gif',
1010 'image/x-png',
1011 );
1012
1013 if (in_array($params[$imageIndex]['type'], $mimeType)) {
5da97e99 1014 $photo = basename($params[$imageIndex]['name']);
9bb77b8e 1015 $params[$imageIndex] = CRM_Utils_System::url('civicrm/contact/imagefile', 'photo=' . $photo, TRUE, NULL, TRUE, TRUE);
6a488035
TO
1016 return TRUE;
1017 }
1018 else {
1019 unset($params[$imageIndex]);
1020 if (!$statusMsg) {
1021 $statusMsg = ts('Image could not be uploaded due to invalid type extension.');
1022 }
1023 if ($opType == 'status') {
1024 CRM_Core_Session::setStatus($statusMsg, 'Sorry', 'error');
1025 }
1026 // FIXME: additional support for fatal, bounce etc could be added.
1027 return FALSE;
1028 }
1029 }
1030
1031 /**
100fef9d 1032 * Extract contact id from url for deleting contact image
6a488035
TO
1033 */
1034 public static function processImage() {
1035
1050d25c
CW
1036 $action = CRM_Utils_Request::retrieve('action', 'String');
1037 $cid = CRM_Utils_Request::retrieve('cid', 'Positive');
6a488035 1038 // retrieve contact id in case of Profile context
1050d25c 1039 $id = CRM_Utils_Request::retrieve('id', 'Positive');
6a488035
TO
1040 $cid = $cid ? $cid : $id;
1041 if ($action & CRM_Core_Action::DELETE) {
1050d25c 1042 if (CRM_Utils_Request::retrieve('confirmed', 'Boolean')) {
6a488035
TO
1043 CRM_Contact_BAO_Contact::deleteContactImage($cid);
1044 CRM_Core_Session::setStatus(ts('Contact image deleted successfully'), ts('Image Deleted'), 'success');
1045 $session = CRM_Core_Session::singleton();
1046 $toUrl = $session->popUserContext();
1047 CRM_Utils_System::redirect($toUrl);
1048 }
1049 }
1050 }
1051
1052 /**
1053 * Function to set is_delete true or restore deleted contact
1054 *
9bb77b8e
TO
1055 * @param int $contact Contact DAO object
1056 * @param boolean $restore true to set the is_delete = 1 else false to restore deleted contact,
6a488035
TO
1057 * i.e. is_delete = 0
1058 *
9bb77b8e 1059 * @return void
6a488035
TO
1060 * @static
1061 */
00be9182 1062 public static function contactTrashRestore($contact, $restore = FALSE) {
6a488035
TO
1063 $op = ($restore ? 'restore' : 'trash');
1064
1065 CRM_Utils_Hook::pre($op, $contact->contact_type, $contact->id, CRM_Core_DAO::$_nullArray);
1066
1067 $params = array(1 => array($contact->id, 'Integer'));
1068 $isDelete = ' is_deleted = 1 ';
1069 if ($restore) {
1070 $isDelete = ' is_deleted = 0 ';
1071 }
1072 else {
1073 $query = "DELETE FROM civicrm_uf_match WHERE contact_id = %1";
1074 CRM_Core_DAO::executeQuery($query, $params);
1075 }
1076
1077 $query = "UPDATE civicrm_contact SET {$isDelete} WHERE id = %1";
1078 CRM_Core_DAO::executeQuery($query, $params);
1079
1080 CRM_Utils_Hook::post($op, $contact->contact_type, $contact->id, $contact);
1081 }
1082
1083 /**
1084 * Get contact type for a contact.
1085 *
77c5b619
TO
1086 * @param int $id
1087 * Id of the contact whose contact type is needed.
6a488035
TO
1088 *
1089 * @return string contact_type if $id found else null ""
1090 *
6a488035
TO
1091 *
1092 * @static
1093 *
1094 */
1095 public static function getContactType($id) {
1096 return CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $id, 'contact_type');
1097 }
1098
1099 /**
1100 * Get contact sub type for a contact.
1101 *
77c5b619
TO
1102 * @param int $id
1103 * Id of the contact whose contact sub type is needed.
6a488035 1104 *
dd244018
EM
1105 * @param null $implodeDelimiter
1106 *
6a488035
TO
1107 * @return string contact_sub_type if $id found else null ""
1108 *
6a488035
TO
1109 *
1110 * @static
6a488035
TO
1111 */
1112 public static function getContactSubType($id, $implodeDelimiter = NULL) {
1113 $subtype = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $id, 'contact_sub_type');
1114 if (!$subtype) {
1115 return $implodeDelimiter ? NULL : array();
1116 }
1117
1118 $subtype = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($subtype, CRM_Core_DAO::VALUE_SEPARATOR));
1119
1120 if ($implodeDelimiter) {
1121 $subtype = implode($implodeDelimiter, $subtype);
1122 }
1123 return $subtype;
1124 }
1125
1126 /**
1127 * Get pair of contact-type and sub-type for a contact.
1128 *
77c5b619
TO
1129 * @param int $id
1130 * Id of the contact whose contact sub/contact type is needed.
6a488035
TO
1131 *
1132 * @return array
1133 *
6a488035
TO
1134 *
1135 * @static
1136 *
1137 */
1138 public static function getContactTypes($id) {
9bb77b8e 1139 $params = array('id' => $id);
6a488035
TO
1140 $details = array();
1141 $contact = CRM_Core_DAO::commonRetrieve('CRM_Contact_DAO_Contact',
1142 $params,
1143 $details,
1144 array('contact_type', 'contact_sub_type')
1145 );
1146
1147 if ($contact) {
1148 $contactTypes = array();
1149 if ($contact->contact_sub_type)
1150 $contactTypes = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($contact->contact_sub_type, CRM_Core_DAO::VALUE_SEPARATOR));
1151 array_unshift($contactTypes, $contact->contact_type);
1152
1153 return $contactTypes;
1154 }
1155 else {
1156 CRM_Core_Error::fatal();
1157 }
1158 }
1159
1160 /**
100fef9d 1161 * Combine all the importable fields from the lower levels object
6a488035
TO
1162 *
1163 * The ordering is important, since currently we do not have a weight
1071730c 1164 * scheme. Adding weight is super important
6a488035 1165 *
dd244018 1166 * @param int|string $contactType contact Type
77c5b619
TO
1167 * @param bool $status
1168 * Status is used to manipulate first title.
1169 * @param bool $showAll
1170 * If true returns all fields (includes disabled fields).
1171 * @param bool $isProfile
1172 * If its profile mode.
1173 * @param bool $checkPermission
1174 * If false, do not include permissioning clause (for custom data).
6a488035 1175 *
dd244018
EM
1176 * @param bool $withMultiCustomFields
1177 *
6a488035 1178 * @return array array of importable Fields
6a488035
TO
1179 * @static
1180 */
9bb77b8e
TO
1181 static function importableFields(
1182 $contactType = 'Individual',
1183 $status = FALSE,
1184 $showAll = FALSE,
1185 $isProfile = FALSE,
6a488035
TO
1186 $checkPermission = TRUE,
1187 $withMultiCustomFields = FALSE
1188 ) {
1189 if (empty($contactType)) {
1190 $contactType = 'All';
1191 }
1192
1193 $cacheKeyString = "importableFields $contactType";
1194 $cacheKeyString .= $status ? '_1' : '_0';
1195 $cacheKeyString .= $showAll ? '_1' : '_0';
1196 $cacheKeyString .= $isProfile ? '_1' : '_0';
1197 $cacheKeyString .= $checkPermission ? '_1' : '_0';
1198
1199 $fields = CRM_Utils_Array::value($cacheKeyString, self::$_importableFields);
1200
1201 if (!$fields) {
1202 // check if we can retrieve from database cache
1203 $fields = CRM_Core_BAO_Cache::getItem('contact fields', $cacheKeyString);
1204 }
1205
1206 if (!$fields) {
1207 $fields = CRM_Contact_DAO_Contact::import();
1208
1209 // get the fields thar are meant for contact types
1210 if (in_array($contactType, array(
9bb77b8e
TO
1211 'Individual',
1212 'Household',
1213 'Organization',
1214 'All'
1215 ))) {
6a488035
TO
1216 $fields = array_merge($fields, CRM_Core_OptionValue::getFields('', $contactType));
1217 }
1218
1219 $locationFields = array_merge(CRM_Core_DAO_Address::import(),
1220 CRM_Core_DAO_Phone::import(),
1221 CRM_Core_DAO_Email::import(),
1222 CRM_Core_DAO_IM::import(TRUE),
1223 CRM_Core_DAO_OpenID::import()
1224 );
1225
1226 $locationFields = array_merge($locationFields,
1227 CRM_Core_BAO_CustomField::getFieldsForImport('Address',
1228 FALSE,
1229 FALSE,
1230 FALSE,
1231 FALSE
1232 )
1233 );
1234
1235 foreach ($locationFields as $key => $field) {
1236 $locationFields[$key]['hasLocationType'] = TRUE;
1237 }
1238
1239 $fields = array_merge($fields, $locationFields);
1240
1071730c
DL
1241 $fields = array_merge($fields, CRM_Contact_DAO_Contact::import());
1242 $fields = array_merge($fields, CRM_Core_DAO_Note::import());
6a488035
TO
1243
1244 //website fields
1245 $fields = array_merge($fields, CRM_Core_DAO_Website::import());
887e764d 1246 $fields['url']['hasWebsiteType'] = TRUE;
6a488035
TO
1247
1248 if ($contactType != 'All') {
1249 $fields = array_merge($fields,
1250 CRM_Core_BAO_CustomField::getFieldsForImport($contactType,
1251 $showAll,
1252 TRUE,
1253 FALSE,
1254 FALSE,
1255 $withMultiCustomFields
1256 )
1257 );
1258 //unset the fields, which are not related to their
1259 //contact type.
1260 $commonValues = array(
8c77c68b
KJ
1261 'Individual' => array(
1262 'household_name',
1263 'legal_name',
1264 'sic_code',
1265 'organization_name'
1266 ),
6a488035 1267 'Household' => array(
8c77c68b
KJ
1268 'first_name',
1269 'middle_name',
1270 'last_name',
e171748b 1271 'formal_title',
8c77c68b 1272 'job_title',
04ffef8d
CW
1273 'gender_id',
1274 'prefix_id',
1275 'suffix_id',
8c77c68b
KJ
1276 'birth_date',
1277 'organization_name',
1278 'legal_name',
1279 'legal_identifier',
1280 'sic_code',
1281 'home_URL',
1282 'is_deceased',
6a488035
TO
1283 'deceased_date',
1284 ),
1285 'Organization' => array(
8c77c68b
KJ
1286 'first_name',
1287 'middle_name',
1288 'last_name',
e171748b 1289 'formal_title',
8c77c68b 1290 'job_title',
04ffef8d
CW
1291 'gender_id',
1292 'prefix_id',
1293 'suffix_id',
8c77c68b
KJ
1294 'birth_date',
1295 'household_name',
1296 'is_deceased',
1297 'deceased_date',
6a488035
TO
1298 ),
1299 );
1300 foreach ($commonValues[$contactType] as $value) {
1301 unset($fields[$value]);
1302 }
1303 }
1304 else {
9bb77b8e 1305 foreach (array('Individual', 'Household', 'Organization') as $type) {
6a488035
TO
1306 $fields = array_merge($fields,
1307 CRM_Core_BAO_CustomField::getFieldsForImport($type,
1308 $showAll,
1309 FALSE,
1310 FALSE,
1311 FALSE,
1312 $withMultiCustomFields
1313 )
1314 );
1315 }
1316 }
1317
1318 if ($isProfile) {
8c77c68b
KJ
1319 $fields = array_merge($fields, array(
1320 'group' => array(
1321 'title' => ts('Group(s)'),
1322 'name' => 'group',
1323 ),
1324 'tag' => array(
1325 'title' => ts('Tag(s)'),
1326 'name' => 'tag',
1327 ),
1328 'note' => array(
1329 'title' => ts('Note(s)'),
1330 'name' => 'note',
1331 ),
d4dcd180 1332 'communication_style_id' => array(
1333 'title' => ts('Communication Style'),
1334 'name' => 'communication_style_id',
1335 ),
8c77c68b 1336 ));
6a488035
TO
1337 }
1338
1339 //Sorting fields in alphabetical order(CRM-1507)
1340 $fields = CRM_Utils_Array::crmArraySortByField($fields, 'title');
1341
1342 CRM_Core_BAO_Cache::setItem($fields, 'contact fields', $cacheKeyString);
1343 }
1344
1345 self::$_importableFields[$cacheKeyString] = $fields;
1346
1347 if (!$isProfile) {
1348 if (!$status) {
1349 $fields = array_merge(array('do_not_import' => array('title' => ts('- do not import -'))),
1350 self::$_importableFields[$cacheKeyString]
1351 );
1352 }
1353 else {
1354 $fields = array_merge(array('' => array('title' => ts('- Contact Fields -'))),
1355 self::$_importableFields[$cacheKeyString]
1356 );
1357 }
1358 }
1359 return $fields;
1360 }
1361
1362 /**
100fef9d 1363 * Combine all the exportable fields from the lower levels object
6a488035
TO
1364 *
1365 * currentlty we are using importable fields as exportable fields
1366 *
8232cf2d 1367 * @param int|string $contactType contact Type
77c5b619
TO
1368 * @param bool $status
1369 * True while exporting primary contacts.
1370 * @param bool $export
1371 * True when used during export.
1372 * @param bool $search
1373 * True when used during search, might conflict with export param?.
6a488035 1374 *
8232cf2d
E
1375 * @param bool $withMultiRecord
1376 *
6a488035 1377 * @return array array of exportable Fields
6a488035
TO
1378 * @static
1379 */
00be9182 1380 public static function &exportableFields($contactType = 'Individual', $status = FALSE, $export = FALSE, $search = FALSE, $withMultiRecord = FALSE) {
6a488035
TO
1381 if (empty($contactType)) {
1382 $contactType = 'All';
1383 }
1384
1385 $cacheKeyString = "exportableFields $contactType";
1386 $cacheKeyString .= $export ? '_1' : '_0';
1387 $cacheKeyString .= $status ? '_1' : '_0';
1388 $cacheKeyString .= $search ? '_1' : '_0';
bb341097
EM
1389 //CRM-14501 it turns out that the impact of permissioning here is sometimes inconsistent. The field that
1390 //calculates custom fields takes into account the logged in user & caches that for all users
1391 //as an interim fix we will cache the fields by contact
1392 $cacheKeyString .= '_' . CRM_Core_Session::getLoggedInContactID();
6a488035
TO
1393
1394 if (!self::$_exportableFields || !CRM_Utils_Array::value($cacheKeyString, self::$_exportableFields)) {
1395 if (!self::$_exportableFields) {
1396 self::$_exportableFields = array();
1397 }
1398
1399 // check if we can retrieve from database cache
1400 $fields = CRM_Core_BAO_Cache::getItem('contact fields', $cacheKeyString);
1401
1402 if (!$fields) {
6a488035
TO
1403 $fields = CRM_Contact_DAO_Contact::export();
1404
1405 // the fields are meant for contact types
9bb77b8e 1406 if (in_array($contactType, array('Individual', 'Household', 'Organization', 'All'))) {
6a488035
TO
1407 $fields = array_merge($fields, CRM_Core_OptionValue::getFields('', $contactType));
1408 }
1409 // add current employer for individuals
1410 $fields = array_merge($fields, array(
1411 'current_employer' =>
1412 array(
1413 'name' => 'organization_name',
1414 'title' => ts('Current Employer'),
1415 ),
9bb77b8e 1416 ));
6a488035
TO
1417
1418 $locationType = array(
8c77c68b
KJ
1419 'location_type' => array(
1420 'name' => 'location_type',
6a488035
TO
1421 'where' => 'civicrm_location_type.name',
1422 'title' => ts('Location Type'),
9bb77b8e
TO
1423 )
1424 );
6a488035
TO
1425
1426 $IMProvider = array(
8c77c68b
KJ
1427 'im_provider' => array(
1428 'name' => 'im_provider',
6a488035
TO
1429 'where' => 'civicrm_im.provider_id',
1430 'title' => ts('IM Provider'),
9bb77b8e
TO
1431 )
1432 );
6a488035
TO
1433
1434 $locationFields = array_merge($locationType,
1435 CRM_Core_DAO_Address::export(),
1436 CRM_Core_DAO_Phone::export(),
1437 CRM_Core_DAO_Email::export(),
1438 $IMProvider,
1439 CRM_Core_DAO_IM::export(TRUE),
1440 CRM_Core_DAO_OpenID::export()
1441 );
1442
1443 $locationFields = array_merge($locationFields,
1444 CRM_Core_BAO_CustomField::getFieldsForImport('Address')
1445 );
1446
1447 foreach ($locationFields as $key => $field) {
1448 $locationFields[$key]['hasLocationType'] = TRUE;
1449 }
1450
1451 $fields = array_merge($fields, $locationFields);
1452
1453 //add world region
1454 $fields = array_merge($fields,
1455 CRM_Core_DAO_Worldregion::export()
1456 );
1457
1458
1459 $fields = array_merge($fields,
1460 CRM_Contact_DAO_Contact::export()
1461 );
1462
1463 //website fields
1464 $fields = array_merge($fields, CRM_Core_DAO_Website::export());
1465
1466 if ($contactType != 'All') {
1467 $fields = array_merge($fields,
b4a3b6b9 1468 CRM_Core_BAO_CustomField::getFieldsForImport($contactType, $status, FALSE, $search, TRUE, $withMultiRecord)
6a488035
TO
1469 );
1470 }
1471 else {
1472 foreach (array(
9bb77b8e
TO
1473 'Individual',
1474 'Household',
1475 'Organization'
1476 ) as $type) {
6a488035
TO
1477 $fields = array_merge($fields,
1478 CRM_Core_BAO_CustomField::getFieldsForImport($type, FALSE, FALSE, $search, TRUE, $withMultiRecord)
1479 );
1480 }
1481 }
1482
1483 //fix for CRM-791
1484 if ($export) {
8c77c68b
KJ
1485 $fields = array_merge($fields, array(
1486 'groups' => array(
1487 'title' => ts('Group(s)'),
1488 'name' => 'groups',
1489 ),
1490 'tags' => array(
1491 'title' => ts('Tag(s)'),
1492 'name' => 'tags',
1493 ),
1494 'notes' => array(
1495 'title' => ts('Note(s)'),
1496 'name' => 'notes',
1497 ),
1498 ));
6a488035
TO
1499 }
1500 else {
8c77c68b
KJ
1501 $fields = array_merge($fields, array(
1502 'group' => array(
1503 'title' => ts('Group(s)'),
1504 'name' => 'group',
1505 ),
1506 'tag' => array(
1507 'title' => ts('Tag(s)'),
1508 'name' => 'tag',
1509 ),
1510 'note' => array(
1511 'title' => ts('Note(s)'),
1512 'name' => 'note',
1513 ),
1514 ));
6a488035
TO
1515 }
1516
1517 //Sorting fields in alphabetical order(CRM-1507)
1518 foreach ($fields as $k => $v) {
1519 $sortArray[$k] = CRM_Utils_Array::value('title', $v);
1520 }
1521
1522 $fields = array_merge($sortArray, $fields);
1523 //unset the field which are not related to their contact type.
1524 if ($contactType != 'All') {
1525 $commonValues = array(
8c77c68b
KJ
1526 'Individual' => array(
1527 'household_name',
1528 'legal_name',
1529 'sic_code',
1530 'organization_name',
1531 'email_greeting_custom',
1532 'postal_greeting_custom',
6a488035
TO
1533 'addressee_custom',
1534 ),
1535 'Household' => array(
8c77c68b
KJ
1536 'first_name',
1537 'middle_name',
1538 'last_name',
e171748b 1539 'formal_title',
8c77c68b 1540 'job_title',
04ffef8d
CW
1541 'gender_id',
1542 'prefix_id',
1543 'suffix_id',
8c77c68b
KJ
1544 'birth_date',
1545 'organization_name',
1546 'legal_name',
1547 'legal_identifier',
1548 'sic_code',
1549 'home_URL',
1550 'is_deceased',
1551 'deceased_date',
1552 'current_employer',
1553 'email_greeting_custom',
1554 'postal_greeting_custom',
1555 'addressee_custom',
1556 'prefix_id',
1557 'suffix_id'
6a488035
TO
1558 ),
1559 'Organization' => array(
8c77c68b
KJ
1560 'first_name',
1561 'middle_name',
1562 'last_name',
e171748b 1563 'formal_title',
8c77c68b 1564 'job_title',
04ffef8d
CW
1565 'gender_id',
1566 'prefix_id',
1567 'suffix_id',
8c77c68b
KJ
1568 'birth_date',
1569 'household_name',
6a488035 1570 'email_greeting_custom',
8c77c68b
KJ
1571 'postal_greeting_custom',
1572 'prefix_id',
1573 'suffix_id',
1574 'gender_id',
1575 'addressee_custom',
1576 'is_deceased',
1577 'deceased_date',
1578 'current_employer',
6a488035
TO
1579 ),
1580 );
1581 foreach ($commonValues[$contactType] as $value) {
1582 unset($fields[$value]);
1583 }
1584 }
1585
1586 CRM_Core_BAO_Cache::setItem($fields, 'contact fields', $cacheKeyString);
1587 }
1588 self::$_exportableFields[$cacheKeyString] = $fields;
1589 }
1590
1591 if (!$status) {
1592 $fields = self::$_exportableFields[$cacheKeyString];
1593 }
1594 else {
1595 $fields = array_merge(array('' => array('title' => ts('- Contact Fields -'))),
1596 self::$_exportableFields[$cacheKeyString]
1597 );
1598 }
1599
1600 return $fields;
1601 }
1602
1603 /**
100fef9d 1604 * Get the all contact details(Hierarchical)
6a488035 1605 *
77c5b619
TO
1606 * @param int $contactId
1607 * Contact id.
1608 * @param array $fields
1609 * Fields array.
6a488035
TO
1610 *
1611 * @return $values array contains the contact details
1612 * @static
6a488035 1613 */
00be9182 1614 public static function getHierContactDetails($contactId, &$fields) {
6a488035
TO
1615 $params = array(array('contact_id', '=', $contactId, 0, 0));
1616 $options = array();
1617
1618 $returnProperties = self::makeHierReturnProperties($fields, $contactId);
1619
1620 // we dont know the contents of return properties, but we need the lower level ids of the contact
1621 // so add a few fields
1622 $returnProperties['first_name'] =
9bb77b8e
TO
1623 $returnProperties['organization_name'] =
1624 $returnProperties['household_name'] =
1625 $returnProperties['contact_type'] =
1626 $returnProperties['contact_sub_type'] = 1;
6a488035
TO
1627 return list($query, $options) = CRM_Contact_BAO_Query::apiQuery($params, $returnProperties, $options);
1628 }
1629
1630 /**
100fef9d 1631 * Given a set of flat profile style field names, create a hierarchy
6a488035
TO
1632 * for query to use and crete the right sql
1633 *
8232cf2d 1634 * @param $fields
77c5b619
TO
1635 * @param int $contactId
1636 * Contact id.
6a488035
TO
1637 *
1638 * @return array a hierarchical property tree if appropriate
6a488035
TO
1639 * @static
1640 */
00be9182 1641 public static function &makeHierReturnProperties($fields, $contactId = NULL) {
b2b0530a 1642 $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
6a488035
TO
1643
1644 $returnProperties = array();
1645
9bb77b8e 1646 $multipleFields = array('website' => 'url');
6a488035
TO
1647 foreach ($fields as $name => $dontCare) {
1648 if (strpos($name, '-') !== FALSE) {
1649 list($fieldName, $id, $type) = CRM_Utils_System::explode('-', $name, 3);
1650
1651 if (!in_array($fieldName, $multipleFields)) {
1652 if ($id == 'Primary') {
1653 $locationTypeName = 1;
1654 }
1655 else {
1656 $locationTypeName = CRM_Utils_Array::value($id, $locationTypes);
1657 if (!$locationTypeName) {
1658 continue;
1659 }
1660 }
1661
a7488080 1662 if (empty($returnProperties['location'])) {
6a488035
TO
1663 $returnProperties['location'] = array();
1664 }
a7488080 1665 if (empty($returnProperties['location'][$locationTypeName])) {
6a488035
TO
1666 $returnProperties['location'][$locationTypeName] = array();
1667 $returnProperties['location'][$locationTypeName]['location_type'] = $id;
1668 }
1669 if (in_array($fieldName, array(
9bb77b8e
TO
1670 'phone',
1671 'im',
1672 'email',
1673 'openid',
1674 'phone_ext'
1675 ))) {
6a488035
TO
1676 if ($type) {
1677 $returnProperties['location'][$locationTypeName][$fieldName . '-' . $type] = 1;
1678 }
1679 else {
1680 $returnProperties['location'][$locationTypeName][$fieldName] = 1;
1681 }
1682 }
1683 elseif (substr($fieldName, 0, 14) === 'address_custom') {
1684 $returnProperties['location'][$locationTypeName][substr($fieldName, 8)] = 1;
1685 }
1686 else {
1687 $returnProperties['location'][$locationTypeName][$fieldName] = 1;
1688 }
1689 }
1690 else {
1691 $returnProperties['website'][$id][$fieldName] = 1;
1692 }
1693 }
1694 else {
1695 $returnProperties[$name] = 1;
1696 }
1697 }
1698
1699 return $returnProperties;
1700 }
1701
1702 /**
100fef9d 1703 * Return the primary location type of a contact
6a488035
TO
1704 *
1705 * $params int $contactId contact_id
1706 * $params boolean $isPrimaryExist if true, return primary contact location type otherwise null
1707 * $params boolean $skipDefaultPriamry if true, return primary contact location type otherwise null
1708 *
100fef9d 1709 * @param int $contactId
8232cf2d
E
1710 * @param bool $skipDefaultPriamry
1711 * @param null $block
1712 *
6a488035 1713 * @return int $locationType location_type_id
6a488035
TO
1714 * @static
1715 */
00be9182 1716 public static function getPrimaryLocationType($contactId, $skipDefaultPriamry = FALSE, $block = NULL) {
9bb77b8e 1717 if ($block) {
6a488035 1718 $entityBlock = array('contact_id' => $contactId);
9bb77b8e
TO
1719 $blocks = CRM_Core_BAO_Location::getValues($entityBlock);
1720 foreach ($blocks[$block] as $key => $value) {
1721 if (!empty($value['is_primary'])) {
1722 $locationType = CRM_Utils_Array::value('location_type_id', $value);
6a488035
TO
1723 }
1724 }
1725 }
1726 else {
1727 $query = "
1728SELECT
1729 IF ( civicrm_email.location_type_id IS NULL,
1730 IF ( civicrm_address.location_type_id IS NULL,
1731 IF ( civicrm_phone.location_type_id IS NULL,
1732 IF ( civicrm_im.location_type_id IS NULL,
1733 IF ( civicrm_openid.location_type_id IS NULL, null, civicrm_openid.location_type_id)
1734 ,civicrm_im.location_type_id)
1735 ,civicrm_phone.location_type_id)
1736 ,civicrm_address.location_type_id)
1737 ,civicrm_email.location_type_id) as locationType
1738FROM civicrm_contact
1739 LEFT JOIN civicrm_email ON ( civicrm_email.is_primary = 1 AND civicrm_email.contact_id = civicrm_contact.id )
1740 LEFT JOIN civicrm_address ON ( civicrm_address.is_primary = 1 AND civicrm_address.contact_id = civicrm_contact.id)
1741 LEFT JOIN civicrm_phone ON ( civicrm_phone.is_primary = 1 AND civicrm_phone.contact_id = civicrm_contact.id)
1742 LEFT JOIN civicrm_im ON ( civicrm_im.is_primary = 1 AND civicrm_im.contact_id = civicrm_contact.id)
1743 LEFT JOIN civicrm_openid ON ( civicrm_openid.is_primary = 1 AND civicrm_openid.contact_id = civicrm_contact.id)
1744WHERE civicrm_contact.id = %1 ";
1745
1746 $params = array(1 => array($contactId, 'Integer'));
1747
1748 $dao = CRM_Core_DAO::executeQuery($query, $params);
1749
1750 $locationType = NULL;
1751 if ($dao->fetch()) {
1752 $locationType = $dao->locationType;
1753 }
1754 }
1755 if (isset($locationType)) {
1756 return $locationType;
1757 }
1758 elseif ($skipDefaultPriamry) {
1759 // if there is no primary contact location then return null
1760 return NULL;
1761 }
1762 else {
1763 // if there is no primart contact location, then return default
1764 // location type of the system
1765 $defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
1766 return $defaultLocationType->id;
1767 }
1768 }
1769
1770 /**
100fef9d 1771 * Get the display name, primary email and location type of a contact
6a488035 1772 *
77c5b619
TO
1773 * @param int $id
1774 * Id of the contact.
6a488035
TO
1775 *
1776 * @return array of display_name, email if found, do_not_email or (null,null,null)
1777 * @static
6a488035 1778 */
00be9182 1779 public static function getContactDetails($id) {
6a488035
TO
1780 // check if the contact type
1781 $contactType = self::getContactType($id);
1782
1783 $nameFields = ($contactType == 'Individual') ? "civicrm_contact.first_name, civicrm_contact.last_name, civicrm_contact.display_name" : "civicrm_contact.display_name";
1784
1785 $sql = "
1786SELECT $nameFields, civicrm_email.email, civicrm_contact.do_not_email, civicrm_email.on_hold, civicrm_contact.is_deceased
1787FROM civicrm_contact LEFT JOIN civicrm_email ON (civicrm_contact.id = civicrm_email.contact_id)
1788WHERE civicrm_contact.id = %1
1789ORDER BY civicrm_email.is_primary DESC";
1790 $params = array(1 => array($id, 'Integer'));
1791 $dao = CRM_Core_DAO::executeQuery($sql, $params);
1792
1793 if ($dao->fetch()) {
1794 if ($contactType == 'Individual') {
1795 if ($dao->first_name || $dao->last_name) {
1796 $name = "{$dao->first_name} {$dao->last_name}";
1797 }
1798 else {
1799 $name = $dao->display_name;
1800 }
1801 }
1802 else {
1803 $name = $dao->display_name;
1804 }
9bb77b8e 1805 $email = $dao->email;
6a488035 1806 $doNotEmail = $dao->do_not_email ? TRUE : FALSE;
9bb77b8e 1807 $onHold = $dao->on_hold ? TRUE : FALSE;
6a488035
TO
1808 $isDeceased = $dao->is_deceased ? TRUE : FALSE;
1809 return array($name, $email, $doNotEmail, $onHold, $isDeceased);
1810 }
1811 return array(NULL, NULL, NULL, NULL, NULL);
1812 }
1813
1814 /**
100fef9d 1815 * Add/edit/register contacts through profile.
6a488035 1816 *
77c5b619
TO
1817 * @param array $params
1818 * Array of profile fields to be edited/added.
1819 * @param array $fields
1820 * Array of fields from UFGroup.
1821 * @param int $contactID
1822 * Id of the contact to be edited/added.
1823 * @param int $addToGroupID
1824 * Specifies the default group to which contact is added.
1825 * @param int $ufGroupId
1826 * Uf group id (profile id).
c490a46a 1827 * @param ctype
77c5b619
TO
1828 * @param bool $visibility
1829 * Basically lets us know where this request is coming from.
6a488035
TO
1830 * if via a profile from web, we restrict what groups are changed
1831 *
c490a46a 1832 * @return int contact id created/edited
6a488035 1833 * @static
6a488035
TO
1834 */
1835 static function createProfileContact(
1836 &$params,
1837 &$fields,
1838 $contactID = NULL,
1839 $addToGroupID = NULL,
1840 $ufGroupId = NULL,
9bb77b8e
TO
1841 $ctype = NULL,
1842 $visibility = FALSE
6a488035
TO
1843 ) {
1844 // add ufGroupID to params array ( CRM-2012 )
1845 if ($ufGroupId) {
1846 $params['uf_group_id'] = $ufGroupId;
1847 }
1848
1849 if ($contactID) {
1850 $editHook = TRUE;
1851 CRM_Utils_Hook::pre('edit', 'Profile', $contactID, $params);
1852 }
1853 else {
1854 $editHook = FALSE;
1855 CRM_Utils_Hook::pre('create', 'Profile', NULL, $params);
1856 }
1857
1858 list($data, $contactDetails) = self::formatProfileContactParams($params, $fields, $contactID, $ufGroupId, $ctype);
1859
1860 // manage is_opt_out
1861 if (array_key_exists('is_opt_out', $fields) && array_key_exists('is_opt_out', $params)) {
9bb77b8e
TO
1862 $wasOptOut = CRM_Utils_Array::value('is_opt_out', $contactDetails, FALSE);
1863 $isOptOut = CRM_Utils_Array::value('is_opt_out', $params, FALSE);
6a488035
TO
1864 $data['is_opt_out'] = $isOptOut;
1865 // on change, create new civicrm_subscription_history entry
8cc574cf 1866 if (($wasOptOut != $isOptOut) && !empty($contactDetails['contact_id'])) {
6a488035
TO
1867 $shParams = array(
1868 'contact_id' => $contactDetails['contact_id'],
1869 'status' => $isOptOut ? 'Removed' : 'Added',
1870 'method' => 'Web',
1871 );
1872 CRM_Contact_BAO_SubscriptionHistory::create($shParams);
1873 }
1874 }
1875
1876 $contact = self::create($data);
1877
1878 // contact is null if the profile does not have any contact fields
1879 if ($contact) {
1880 $contactID = $contact->id;
1881 }
1882
1883 if (empty($contactID)) {
1884 CRM_Core_Error::fatal('Cannot proceed without a valid contact id');
1885 }
1886
1887 // Process group and tag
a7488080 1888 if (!empty($fields['group'])) {
6a488035
TO
1889 $method = 'Admin';
1890 // this for sure means we are coming in via profile since i added it to fix
1891 // removing contacts from user groups -- lobo
1892 if ($visibility) {
1893 $method = 'Web';
1894 }
1895 CRM_Contact_BAO_GroupContact::create($params['group'], $contactID, $visibility, $method);
1896 }
1897
a7488080 1898 if (!empty($fields['tag'])) {
6a488035
TO
1899 CRM_Core_BAO_EntityTag::create($params['tag'], 'civicrm_contact', $contactID);
1900 }
1901
1902 //to add profile in default group
1903 if (is_array($addToGroupID)) {
1904 $contactIds = array($contactID);
1905 foreach ($addToGroupID as $groupId) {
1906 CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $groupId);
1907 }
1908 }
1909 elseif ($addToGroupID) {
1910 $contactIds = array($contactID);
1911 CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $addToGroupID);
1912 }
1913
1914 // reset the group contact cache for this group
1915 CRM_Contact_BAO_GroupContactCache::remove();
1916
1917 if ($editHook) {
1918 CRM_Utils_Hook::post('edit', 'Profile', $contactID, $params);
1919 }
1920 else {
1921 CRM_Utils_Hook::post('create', 'Profile', $contactID, $params);
1922 }
1923 return $contactID;
1924 }
1925
86538308 1926 /**
c490a46a 1927 * @param array $params
86538308 1928 * @param $fields
100fef9d
CW
1929 * @param int $contactID
1930 * @param int $ufGroupId
86538308
EM
1931 * @param null $ctype
1932 * @param bool $skipCustom
1933 *
1934 * @return array
1935 */
6a488035
TO
1936 static function formatProfileContactParams(
1937 &$params,
1938 &$fields,
1939 $contactID = NULL,
1940 $ufGroupId = NULL,
1941 $ctype = NULL,
1942 $skipCustom = FALSE
1943 ) {
1944
1945 $data = $contactDetails = array();
1946
1947 // get the contact details (hier)
1948 if ($contactID) {
1949 list($details, $options) = self::getHierContactDetails($contactID, $fields);
1950
1951 $contactDetails = $details[$contactID];
1952 $data['contact_type'] = CRM_Utils_Array::value('contact_type', $contactDetails);
1953 $data['contact_sub_type'] = CRM_Utils_Array::value('contact_sub_type', $contactDetails);
1954 }
1955 else {
1956 //we should get contact type only if contact
1957 if ($ufGroupId) {
1958 $data['contact_type'] = CRM_Core_BAO_UFField::getProfileType($ufGroupId);
1959
1960 //special case to handle profile with only contact fields
1961 if ($data['contact_type'] == 'Contact') {
1962 $data['contact_type'] = 'Individual';
1963 }
1964 elseif (CRM_Contact_BAO_ContactType::isaSubType($data['contact_type'])) {
1965 $data['contact_type'] = CRM_Contact_BAO_ContactType::getBasicType($data['contact_type']);
1966 }
1967 }
1968 elseif ($ctype) {
1969 $data['contact_type'] = $ctype;
1970 }
1971 else {
1972 $data['contact_type'] = 'Individual';
1973 }
1974 }
1975
1976 //fix contact sub type CRM-5125
1977 if (array_key_exists('contact_sub_type', $params) &&
1978 !empty($params['contact_sub_type'])
1979 ) {
9bb77b8e 1980 $data['contact_sub_type'] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, (array) $params['contact_sub_type']) . CRM_Core_DAO::VALUE_SEPARATOR;
6a488035
TO
1981 }
1982 elseif (array_key_exists('contact_sub_type_hidden', $params) &&
1983 !empty($params['contact_sub_type_hidden'])
1984 ) {
1985 // if profile was used, and had any subtype, we obtain it from there
9bb77b8e 1986 $data['contact_sub_type'] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, (array) $params['contact_sub_type_hidden']) . CRM_Core_DAO::VALUE_SEPARATOR;
6a488035
TO
1987 }
1988
1989 if ($ctype == 'Organization') {
1990 $data['organization_name'] = CRM_Utils_Array::value('organization_name', $contactDetails);
1991 }
1992 elseif ($ctype == 'Household') {
1993 $data['household_name'] = CRM_Utils_Array::value('household_name', $contactDetails);
1994 }
1995
1996 $locationType = array();
1997 $count = 1;
1998
1999 if ($contactID) {
2000 //add contact id
2001 $data['contact_id'] = $contactID;
2002 $primaryLocationType = self::getPrimaryLocationType($contactID);
2003 }
2004 else {
2005 $defaultLocation = CRM_Core_BAO_LocationType::getDefault();
2006 $defaultLocationId = $defaultLocation->id;
2007 }
2008
2009 // get the billing location type
180409a4 2010 $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', array(), 'validate');
6a488035
TO
2011 $billingLocationTypeId = array_search('Billing', $locationTypes);
2012
2013 $blocks = array('email', 'phone', 'im', 'openid');
2014
2015 $multiplFields = array('url');
2016 // prevent overwritten of formatted array, reset all block from
2017 // params if it is not in valid format (since import pass valid format)
2018 foreach ($blocks as $blk) {
2019 if (array_key_exists($blk, $params) &&
2020 !is_array($params[$blk])
2021 ) {
2022 unset($params[$blk]);
2023 }
2024 }
2025
2026 $primaryPhoneLoc = NULL;
ece0bc24 2027 $session = CRM_Core_Session::singleton();
6a488035
TO
2028 foreach ($params as $key => $value) {
2029 $fieldName = $locTypeId = $typeId = NULL;
2030 list($fieldName, $locTypeId, $typeId) = CRM_Utils_System::explode('-', $key, 3);
2031
2032 //store original location type id
2033 $actualLocTypeId = $locTypeId;
2034
2035 if ($locTypeId == 'Primary') {
2036 if ($contactID) {
9bb77b8e 2037 if (in_array($fieldName, $blocks)) {
6a488035
TO
2038 $locTypeId = self::getPrimaryLocationType($contactID, FALSE, $fieldName);
2039 }
9bb77b8e 2040 else {
6a488035
TO
2041 $locTypeId = self::getPrimaryLocationType($contactID, FALSE, 'address');
2042 }
2043 $primaryLocationType = $locTypeId;
2044 }
2045 else {
2046 $locTypeId = $defaultLocationId;
2047 }
2048 }
2049
2050 if (is_numeric($locTypeId) &&
2051 !in_array($fieldName, $multiplFields) &&
2052 substr($fieldName, 0, 7) != 'custom_'
2053 ) {
2054 $index = $locTypeId;
2055
2056 if (is_numeric($typeId)) {
2057 $index .= '-' . $typeId;
2058 }
2059 if (!in_array($index, $locationType)) {
2060 $locationType[$count] = $index;
2061 $count++;
2062 }
2063
2064 $loc = CRM_Utils_Array::key($index, $locationType);
2065
9bb77b8e 2066 $blockName = in_array($fieldName, $blocks) ? $fieldName : 'address';
6a488035
TO
2067
2068 $data[$blockName][$loc]['location_type_id'] = $locTypeId;
2069
2070 //set is_billing true, for location type "Billing"
2071 if ($locTypeId == $billingLocationTypeId) {
2072 $data[$blockName][$loc]['is_billing'] = 1;
2073 }
2074
2075 if ($contactID) {
2076 //get the primary location type
2077 if ($locTypeId == $primaryLocationType) {
2078 $data[$blockName][$loc]['is_primary'] = 1;
2079 }
2080 }
2081 elseif ($locTypeId == $defaultLocationId) {
2082 $data[$blockName][$loc]['is_primary'] = 1;
2083 }
2084
9bb77b8e 2085 if (in_array($fieldName, array('phone'))) {
6a488035
TO
2086 if ($typeId) {
2087 $data['phone'][$loc]['phone_type_id'] = $typeId;
2088 }
2089 else {
2090 $data['phone'][$loc]['phone_type_id'] = '';
2091 }
2092 $data['phone'][$loc]['phone'] = $value;
2093
2094 //special case to handle primary phone with different phone types
2095 // in this case we make first phone type as primary
2096 if (isset($data['phone'][$loc]['is_primary']) && !$primaryPhoneLoc) {
2097 $primaryPhoneLoc = $loc;
2098 }
2099
2100 if ($loc != $primaryPhoneLoc) {
2101 unset($data['phone'][$loc]['is_primary']);
2102 }
2103 }
2104 elseif ($fieldName == 'phone_ext') {
2105 $data['phone'][$loc]['phone_ext'] = $value;
2106 }
2107 elseif ($fieldName == 'email') {
2108 $data['email'][$loc]['email'] = $value;
2109 }
2110 elseif ($fieldName == 'im') {
2111 if (isset($params[$key . '-provider_id'])) {
2112 $data['im'][$loc]['provider_id'] = $params[$key . '-provider_id'];
2113 }
2114 if (strpos($key, '-provider_id') !== FALSE) {
2115 $data['im'][$loc]['provider_id'] = $params[$key];
2116 }
2117 else {
2118 $data['im'][$loc]['name'] = $value;
2119 }
2120 }
2121 elseif ($fieldName == 'openid') {
2122 $data['openid'][$loc]['openid'] = $value;
2123 }
2124 else {
2125 if ($fieldName === 'state_province') {
2126 // CRM-3393
2127 if (is_numeric($value) && ((int ) $value) >= 1000) {
2128 $data['address'][$loc]['state_province_id'] = $value;
2129 }
2130 elseif (empty($value)) {
2131 $data['address'][$loc]['state_province_id'] = '';
2132 }
2133 else {
2134 $data['address'][$loc]['state_province'] = $value;
2135 }
2136 }
2137 elseif ($fieldName === 'country') {
2138 // CRM-3393
2139 if (is_numeric($value) && ((int ) $value) >= 1000
2140 ) {
2141 $data['address'][$loc]['country_id'] = $value;
2142 }
2143 elseif (empty($value)) {
2144 $data['address'][$loc]['country_id'] = '';
2145 }
2146 else {
2147 $data['address'][$loc]['country'] = $value;
2148 }
2149 }
2150 elseif ($fieldName === 'county') {
2151 $data['address'][$loc]['county_id'] = $value;
2152 }
2153 elseif ($fieldName == 'address_name') {
2154 $data['address'][$loc]['name'] = $value;
2155 }
2156 elseif (substr($fieldName, 0, 14) === 'address_custom') {
2157 $data['address'][$loc][substr($fieldName, 8)] = $value;
2158 }
2159 else {
2160 $data['address'][$loc][$fieldName] = $value;
2161 }
2162 }
2163 }
2164 else {
2165 if (substr($key, 0, 4) === 'url-') {
2166 $websiteField = explode('-', $key);
887e764d
PN
2167 $data['website'][$websiteField[1]]['website_type_id'] = $websiteField[1];
2168 $data['website'][$websiteField[1]]['url'] = $value;
6a488035 2169 }
6a488035 2170 elseif (in_array($key, self::$_greetingTypes, TRUE)) {
41665e8f 2171 //save email/postal greeting and addressee values if any, CRM-4575
6a488035
TO
2172 $data[$key . '_id'] = $value;
2173 }
2174 elseif (!$skipCustom && ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key))) {
2175 // for autocomplete transfer hidden value instead of label
2176 if ($params[$key] && isset($params[$key . '_id'])) {
2177 $value = $params[$key . '_id'];
2178 }
2179
2180 // we need to append time with date
2181 if ($params[$key] && isset($params[$key . '_time'])) {
2182 $value .= ' ' . $params[$key . '_time'];
2183 }
2184
ece0bc24
DS
2185 // if auth source is not checksum / login && $value is blank, do not proceed - CRM-10128
2186 if (($session->get('authSrc') & (CRM_Core_Permission::AUTH_SRC_CHECKSUM + CRM_Core_Permission::AUTH_SRC_LOGIN)) == 0 &&
9bb77b8e
TO
2187 ($value == '' || !isset($value))
2188 ) {
ece0bc24 2189 continue;
69739cb8 2190 }
ece0bc24 2191
6a488035 2192 $valueId = NULL;
a7488080 2193 if (!empty($params['customRecordValues'])) {
6a488035
TO
2194 if (is_array($params['customRecordValues']) && !empty($params['customRecordValues'])) {
2195 foreach ($params['customRecordValues'] as $recId => $customFields) {
2196 if (is_array($customFields) && !empty($customFields)) {
2197 foreach ($customFields as $customFieldName) {
2198 if ($customFieldName == $key) {
2199 $valueId = $recId;
2200 break;
2201 }
2202 }
2203 }
2204 }
2205 }
2206 }
2207
2208 $type = $data['contact_type'];
a7488080 2209 if (!empty($data['contact_sub_type'])) {
6a488035
TO
2210 $type = $data['contact_sub_type'];
2211 $type = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($type, CRM_Core_DAO::VALUE_SEPARATOR));
2212 // generally a contact even if, has multiple subtypes the parent-type is going to be one only
2213 // and since formatCustomField() would be interested in parent type, lets consider only one subtype
2214 // as the results going to be same.
2215 $type = $type[0];
2216 }
2217
2218 CRM_Core_BAO_CustomField::formatCustomField($customFieldId,
2219 $data['custom'],
2220 $value,
2221 $type,
2222 $valueId,
2223 $contactID
2224 );
2225 }
2226 elseif ($key == 'edit') {
2227 continue;
2228 }
2229 else {
2230 if ($key == 'location') {
2231 foreach ($value as $locationTypeId => $field) {
2232 foreach ($field as $block => $val) {
2233 if ($block == 'address' && array_key_exists('address_name', $val)) {
2234 $value[$locationTypeId][$block]['name'] = $value[$locationTypeId][$block]['address_name'];
2235 }
2236 }
2237 }
2238 }
9bb77b8e 2239 if ($key == 'phone' && isset($params['phone_ext'])) {
6a488035 2240 $data[$key] = $value;
9bb77b8e
TO
2241 foreach ($value as $cnt => $phoneBlock) {
2242 if ($params[$key][$cnt]['location_type_id'] == $params['phone_ext'][$cnt]['location_type_id']) {
6a488035
TO
2243 $data[$key][$cnt]['phone_ext'] = CRM_Utils_Array::retrieveValueRecursive($params['phone_ext'][$cnt], 'phone_ext');
2244 }
2245 }
2246 }
69739cb8 2247 else if (in_array($key,
9bb77b8e
TO
2248 array(
2249 'nick_name',
69739cb8
EM
2250 'job_title',
2251 'middle_name',
2252 'birth_date',
4512fd44 2253 'gender_id',
69739cb8
EM
2254 'current_employer',
2255 'prefix_id',
9bb77b8e
TO
2256 'suffix_id'
2257 )) &&
55ff2e3f 2258 ($value == '' || !isset($value)) &&
9bb77b8e
TO
2259 ($session->get('authSrc') & (CRM_Core_Permission::AUTH_SRC_CHECKSUM + CRM_Core_Permission::AUTH_SRC_LOGIN)) == 0
2260 ) {
69739cb8 2261 // CRM-10128: if auth source is not checksum / login && $value is blank, do not fill $data with empty value
55ff2e3f
DS
2262 // to avoid update with empty values
2263 continue;
69739cb8 2264 }
6a488035
TO
2265 else {
2266 $data[$key] = $value;
2267 }
2268 }
2269 }
2270 }
2271
2272 if (!isset($data['contact_type'])) {
2273 $data['contact_type'] = 'Individual';
2274 }
2275
2276 //set the values for checkboxes (do_not_email, do_not_mail, do_not_trade, do_not_phone)
2277 $privacy = CRM_Core_SelectValues::privacy();
2278 foreach ($privacy as $key => $value) {
2279 if (array_key_exists($key, $fields)) {
29748d00 2280 // do not reset values for existing contacts, if fields are added to a profile
6a488035
TO
2281 if (array_key_exists($key, $params)) {
2282 $data[$key] = $params[$key];
29748d00
C
2283 if (empty($params[$key])) {
2284 $data[$key] = 0;
2285 }
6a488035
TO
2286 }
2287 elseif (!$contactID) {
2288 $data[$key] = 0;
2289 }
2290 }
2291 }
2292
2293 return array($data, $contactDetails);
2294 }
2295
2296 /**
100fef9d 2297 * Find the get contact details
6a488035
TO
2298 * does not respect ACLs for now, which might need to be rectified at some
2299 * stage based on how its used
2300 *
77c5b619
TO
2301 * @param string $mail
2302 * Primary email address of the contact.
2303 * @param string $ctype
2304 * Contact type.
6a488035
TO
2305 *
2306 * @return object $dao contact details
2307 * @static
2308 */
00be9182 2309 public static function &matchContactOnEmail($mail, $ctype = NULL) {
6a488035 2310 $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
9bb77b8e
TO
2311 $mail = $strtolower(trim($mail));
2312 $query = "
6a488035
TO
2313SELECT civicrm_contact.id as contact_id,
2314 civicrm_contact.hash as hash,
2315 civicrm_contact.contact_type as contact_type,
2316 civicrm_contact.contact_sub_type as contact_sub_type
2317FROM civicrm_contact
2318INNER JOIN civicrm_email ON ( civicrm_contact.id = civicrm_email.contact_id )";
2319
2320
9bb77b8e 2321 if (CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MULTISITE_PREFERENCES_NAME, 'uniq_email_per_site')) {
6a488035
TO
2322 // try to find a match within a site (multisite).
2323 $groups = CRM_Core_BAO_Domain::getChildGroupIds();
2324 if (!empty($groups)) {
2325 $query .= "
2326INNER JOIN civicrm_group_contact gc ON
2327(civicrm_contact.id = gc.contact_id AND gc.status = 'Added' AND gc.group_id IN (" . implode(',', $groups) . "))";
2328 }
2329 }
2330
2331 $query .= "
2332WHERE civicrm_email.email = %1 AND civicrm_contact.is_deleted=0";
2333 $p = array(1 => array($mail, 'String'));
2334
2335 if ($ctype) {
2336 $query .= " AND civicrm_contact.contact_type = %3";
2337 $p[3] = array($ctype, 'String');
2338 }
2339
2340 $query .= " ORDER BY civicrm_email.is_primary DESC";
2341
2342 $dao = CRM_Core_DAO::executeQuery($query, $p);
2343
2344 if ($dao->fetch()) {
2345 return $dao;
2346 }
2347 return CRM_Core_DAO::$_nullObject;
2348 }
2349
2350 /**
100fef9d 2351 * Find the contact details associated with an OpenID
6a488035 2352 *
77c5b619
TO
2353 * @param string $openId
2354 * OpenId of the contact.
2355 * @param string $ctype
2356 * Contact type.
6a488035
TO
2357 *
2358 * @return object $dao contact details
2359 * @static
2360 */
00be9182 2361 public static function &matchContactOnOpenId($openId, $ctype = NULL) {
6a488035 2362 $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
9bb77b8e
TO
2363 $openId = $strtolower(trim($openId));
2364 $query = "
6a488035
TO
2365SELECT civicrm_contact.id as contact_id,
2366 civicrm_contact.hash as hash,
2367 civicrm_contact.contact_type as contact_type,
2368 civicrm_contact.contact_sub_type as contact_sub_type
2369FROM civicrm_contact
2370INNER JOIN civicrm_openid ON ( civicrm_contact.id = civicrm_openid.contact_id )
2371WHERE civicrm_openid.openid = %1";
2372 $p = array(1 => array($openId, 'String'));
2373
2374 if ($ctype) {
2375 $query .= " AND civicrm_contact.contact_type = %3";
2376 $p[3] = array($ctype, 'String');
2377 }
2378
2379 $query .= " ORDER BY civicrm_openid.is_primary DESC";
2380
2381 $dao = CRM_Core_DAO::executeQuery($query, $p);
2382
2383 if ($dao->fetch()) {
2384 return $dao;
2385 }
2386 return CRM_Core_DAO::$_nullObject;
2387 }
2388
2389 /**
2390 * Funtion to get primary email of the contact
2391 *
77c5b619
TO
2392 * @param int $contactID
2393 * Contact id.
6a488035
TO
2394 *
2395 * @return string $dao->email email address if present else null
2396 * @static
6a488035
TO
2397 */
2398 public static function getPrimaryEmail($contactID) {
2399 // fetch the primary email
2400 $query = "
2401 SELECT civicrm_email.email as email
2402 FROM civicrm_contact
2403LEFT JOIN civicrm_email ON ( civicrm_contact.id = civicrm_email.contact_id )
2404 WHERE civicrm_email.is_primary = 1
2405 AND civicrm_contact.id = %1";
2406 $p = array(1 => array($contactID, 'Integer'));
2407 $dao = CRM_Core_DAO::executeQuery($query, $p);
2408
2409 $email = NULL;
2410 if ($dao->fetch()) {
2411 $email = $dao->email;
2412 }
2413 $dao->free();
2414 return $email;
2415 }
2416
2417 /**
2418 * Funtion to get primary OpenID of the contact
2419 *
77c5b619
TO
2420 * @param int $contactID
2421 * Contact id.
6a488035
TO
2422 *
2423 * @return string $dao->openid OpenID if present else null
2424 * @static
6a488035
TO
2425 */
2426 public static function getPrimaryOpenId($contactID) {
2427 // fetch the primary OpenID
2428 $query = "
2429SELECT civicrm_openid.openid as openid
2430FROM civicrm_contact
2431LEFT JOIN civicrm_openid ON ( civicrm_contact.id = civicrm_openid.contact_id )
2432WHERE civicrm_contact.id = %1
2433AND civicrm_openid.is_primary = 1";
2434 $p = array(1 => array($contactID, 'Integer'));
2435 $dao = CRM_Core_DAO::executeQuery($query, $p);
2436
2437 $openId = NULL;
2438 if ($dao->fetch()) {
2439 $openId = $dao->openid;
2440 }
2441 $dao->free();
2442 return $openId;
2443 }
2444
2445 /**
2446 * Given the list of params in the params array, fetch the object
2447 * and store the values in the values array
2448 *
77c5b619
TO
2449 * @param array $params
2450 * Input parameters to find object.
2451 * @param array $values
2452 * Output values of the object.
6a488035
TO
2453 *
2454 * @return CRM_Contact_BAO_Contact|null the found object or null
6a488035
TO
2455 * @static
2456 */
2457 public static function getValues(&$params, &$values) {
2458 $contact = new CRM_Contact_BAO_Contact();
2459
2460 $contact->copyValues($params);
2461
2462 if ($contact->find(TRUE)) {
2463
2464 CRM_Core_DAO::storeValues($contact, $values);
2465
2466 $privacy = array();
2467 foreach (self::$_commPrefs as $name) {
2468 if (isset($contact->$name)) {
2469 $privacy[$name] = $contact->$name;
2470 }
2471 }
2472
2473 if (!empty($privacy)) {
2474 $values['privacy'] = $privacy;
2475 }
2476
2477 // communication Prefferance
2478 $preffComm = $comm = array();
2479 $comm = explode(CRM_Core_DAO::VALUE_SEPARATOR,
2480 $contact->preferred_communication_method
2481 );
2482 foreach ($comm as $value) {
2483 $preffComm[$value] = 1;
2484 }
2485 $temp = array('preferred_communication_method' => $contact->preferred_communication_method);
2486
2487 $names = array(
9bb77b8e
TO
2488 'preferred_communication_method' => array(
2489 'newName' => 'preferred_communication_method_display',
6a488035 2490 'groupName' => 'preferred_communication_method',
9bb77b8e
TO
2491 )
2492 );
6a488035
TO
2493
2494 CRM_Core_OptionGroup::lookupValues($temp, $names, FALSE);
2495
2496 $values['preferred_communication_method'] = $preffComm;
2497 $values['preferred_communication_method_display'] = CRM_Utils_Array::value('preferred_communication_method_display', $temp);
2498
77d0b1f8 2499 $preferredMailingFormat = CRM_Core_SelectValues::pmf();
2500 $values['preferred_mail_format'] = $preferredMailingFormat[$contact->preferred_mail_format];
6a488035
TO
2501
2502 // get preferred languages
2503 if (!empty($contact->preferred_language)) {
a8c23526 2504 $values['preferred_language'] = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', 'preferred_language', $contact->preferred_language);
6a488035
TO
2505 }
2506
2507 // Calculating Year difference
2508 if ($contact->birth_date) {
2509 $birthDate = CRM_Utils_Date::customFormat($contact->birth_date, '%Y%m%d');
2510 if ($birthDate < date('Ymd')) {
9bb77b8e 2511 $age = CRM_Utils_Date::calculateAge($birthDate);
6a488035
TO
2512 $values['age']['y'] = CRM_Utils_Array::value('years', $age);
2513 $values['age']['m'] = CRM_Utils_Array::value('months', $age);
2514 }
2515
2516 list($values['birth_date']) = CRM_Utils_Date::setDateDefaults($contact->birth_date, 'birth');
2517 $values['birth_date_display'] = $contact->birth_date;
2518 }
2519
2520 if ($contact->deceased_date) {
2521 list($values['deceased_date']) = CRM_Utils_Date::setDateDefaults($contact->deceased_date, 'birth');
2522 $values['deceased_date_display'] = $contact->deceased_date;
2523 }
2524
2525 $contact->contact_id = $contact->id;
2526
2527 return $contact;
2528 }
2529 return NULL;
2530 }
2531
2532 /**
2533 * Given the component name and returns
2534 * the count of participation of contact
2535 *
77c5b619
TO
2536 * @param string $component
2537 * Input component name.
2538 * @param int $contactId
2539 * Input contact id.
2540 * @param string $tableName
2541 * Optional tableName if component is custom group.
6a488035
TO
2542 *
2543 * @return total number of count of occurence in database
6a488035
TO
2544 * @static
2545 */
00be9182 2546 public static function getCountComponent($component, $contactId, $tableName = NULL) {
6a488035
TO
2547 $object = NULL;
2548 switch ($component) {
2549 case 'tag':
2550 return CRM_Core_BAO_EntityTag::getContactTags($contactId, TRUE);
2551
2552 case 'rel':
2553 return CRM_Contact_BAO_Relationship::getRelationship($contactId,
2554 CRM_Contact_BAO_Relationship::CURRENT,
2555 0, 1
2556 );
2557
2558 case 'group':
2559 return CRM_Contact_BAO_GroupContact::getContactGroup($contactId, "Added", NULL, TRUE);
2560
2561 case 'log':
2562 if (CRM_Core_BAO_Log::useLoggingReport()) {
2563 return FALSE;
2564 }
2565 return CRM_Core_BAO_Log::getContactLogCount($contactId);
2566
2567 case 'note':
2568 return CRM_Core_BAO_Note::getContactNoteCount($contactId);
2569
2570 case 'contribution':
2571 return CRM_Contribute_BAO_Contribution::contributionCount($contactId);
2572
2573 case 'membership':
2574 return CRM_Member_BAO_Membership::getContactMembershipCount($contactId, TRUE);
2575
2576 case 'participant':
2577 return CRM_Event_BAO_Participant::getContactParticipantCount($contactId);
2578
2579 case 'pledge':
2580 return CRM_Pledge_BAO_Pledge::getContactPledgeCount($contactId);
2581
2582 case 'case':
2583 return CRM_Case_BAO_Case::caseCount($contactId);
2584
2585 case 'grant':
2586 return CRM_Grant_BAO_Grant::getContactGrantCount($contactId);
2587
2588 case 'activity':
2589 $input = array(
2590 'contact_id' => $contactId,
2591 'admin' => FALSE,
2592 'caseId' => NULL,
2593 'context' => 'activity',
2594 );
2595 return CRM_Activity_BAO_Activity::getActivitiesCount($input);
2596
36bb946c 2597 case 'mailing':
7c006637 2598 $params = array('contact_id' => $contactId);
553f116a 2599 return CRM_Mailing_BAO_Mailing::getContactMailingsCount($params);
36bb946c 2600
6a488035
TO
2601 default:
2602 $custom = explode('_', $component);
2603 if ($custom['0'] = 'custom') {
2604 if (!$tableName) {
2605 $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $custom['1'], 'table_name');
2606 }
2607 $queryString = "SELECT count(id) FROM {$tableName} WHERE entity_id = {$contactId}";
2608 return CRM_Core_DAO::singleValueQuery($queryString);
2609 }
2610 }
2611 }
2612
2613 /**
100fef9d 2614 * Process greetings and cache
6a488035 2615 *
77c5b619
TO
2616 * @param object $contact
2617 * Contact object after save.
2618 * @param bool $useDefaults
2619 * Use default greeting values.
6a488035
TO
2620 *
2621 * @return void
6a488035
TO
2622 * @static
2623 */
00be9182 2624 public static function processGreetings(&$contact, $useDefaults = FALSE) {
6a488035
TO
2625 if ($useDefaults) {
2626 //retrieve default greetings
2627 $defaultGreetings = CRM_Core_PseudoConstant::greetingDefaults();
2628 $contactDefaults = $defaultGreetings[$contact->contact_type];
2629 }
2630
2631 // note that contact object not always has required greeting related
2632 // fields that are required to calculate greeting and
2633 // also other fields used in tokens etc,
2634 // hence we need to retrieve it again.
9bb77b8e 2635 if ($contact->_query !== FALSE) {
1a902da0
BS
2636 $contact->find(TRUE);
2637 }
6a488035
TO
2638
2639 // store object values to an array
2640 $contactDetails = array();
2641 CRM_Core_DAO::storeValues($contact, $contactDetails);
2642 $contactDetails = array(array($contact->id => $contactDetails));
2643
2644 $emailGreetingString = $postalGreetingString = $addresseeString = NULL;
2645 $updateQueryString = array();
2646
2647 //cache email and postal greeting to greeting display
2648 if ($contact->email_greeting_custom != 'null' && $contact->email_greeting_custom) {
2649 $emailGreetingString = $contact->email_greeting_custom;
2650 }
2651 elseif ($contact->email_greeting_id != 'null' && $contact->email_greeting_id) {
2652 // the filter value for Individual contact type is set to 1
2653 $filter = array(
2654 'contact_type' => $contact->contact_type,
2655 'greeting_type' => 'email_greeting',
2656 );
2657
9bb77b8e 2658 $emailGreeting = CRM_Core_PseudoConstant::greeting($filter);
6a488035
TO
2659 $emailGreetingString = $emailGreeting[$contact->email_greeting_id];
2660 $updateQueryString[] = " email_greeting_custom = NULL ";
2661 }
2662 else {
2663 if ($useDefaults) {
2664 reset($contactDefaults['email_greeting']);
9bb77b8e 2665 $emailGreetingID = key($contactDefaults['email_greeting']);
6a488035
TO
2666 $emailGreetingString = $contactDefaults['email_greeting'][$emailGreetingID];
2667 $updateQueryString[] = " email_greeting_id = $emailGreetingID ";
2668 $updateQueryString[] = " email_greeting_custom = NULL ";
2669 }
2670 elseif ($contact->email_greeting_custom) {
2671 $updateQueryString[] = " email_greeting_display = NULL ";
2672 }
2673 }
2674
2675 if ($emailGreetingString) {
73d64eb6 2676 CRM_Contact_BAO_Contact_Utils::processGreetingTemplate($emailGreetingString,
6a488035
TO
2677 $contactDetails,
2678 $contact->id,
2679 'CRM_Contact_BAO_Contact'
2680 );
2681 $emailGreetingString = CRM_Core_DAO::escapeString(CRM_Utils_String::stripSpaces($emailGreetingString));
2682 $updateQueryString[] = " email_greeting_display = '{$emailGreetingString}'";
2683 }
2684
2685 //postal greetings
2686 if ($contact->postal_greeting_custom != 'null' && $contact->postal_greeting_custom) {
2687 $postalGreetingString = $contact->postal_greeting_custom;
2688 }
2689 elseif ($contact->postal_greeting_id != 'null' && $contact->postal_greeting_id) {
2690 $filter = array(
2691 'contact_type' => $contact->contact_type,
2692 'greeting_type' => 'postal_greeting',
2693 );
2694 $postalGreeting = CRM_Core_PseudoConstant::greeting($filter);
2695 $postalGreetingString = $postalGreeting[$contact->postal_greeting_id];
2696 $updateQueryString[] = " postal_greeting_custom = NULL ";
2697 }
2698 else {
2699 if ($useDefaults) {
2700 reset($contactDefaults['postal_greeting']);
2701 $postalGreetingID = key($contactDefaults['postal_greeting']);
2702 $postalGreetingString = $contactDefaults['postal_greeting'][$postalGreetingID];
2703 $updateQueryString[] = " postal_greeting_id = $postalGreetingID ";
2704 $updateQueryString[] = " postal_greeting_custom = NULL ";
2705 }
2706 elseif ($contact->postal_greeting_custom) {
2707 $updateQueryString[] = " postal_greeting_display = NULL ";
2708 }
2709 }
2710
2711 if ($postalGreetingString) {
73d64eb6 2712 CRM_Contact_BAO_Contact_Utils::processGreetingTemplate($postalGreetingString,
6a488035
TO
2713 $contactDetails,
2714 $contact->id,
2715 'CRM_Contact_BAO_Contact'
2716 );
2717 $postalGreetingString = CRM_Core_DAO::escapeString(CRM_Utils_String::stripSpaces($postalGreetingString));
2718 $updateQueryString[] = " postal_greeting_display = '{$postalGreetingString}'";
2719 }
2720
2721 // addressee
2722 if ($contact->addressee_custom != 'null' && $contact->addressee_custom) {
2723 $addresseeString = $contact->addressee_custom;
2724 }
2725 elseif ($contact->addressee_id != 'null' && $contact->addressee_id) {
2726 $filter = array(
2727 'contact_type' => $contact->contact_type,
2728 'greeting_type' => 'addressee',
2729 );
2730
9bb77b8e
TO
2731 $addressee = CRM_Core_PseudoConstant::greeting($filter);
2732 $addresseeString = $addressee[$contact->addressee_id];
6a488035
TO
2733 $updateQueryString[] = " addressee_custom = NULL ";
2734 }
2735 else {
2736 if ($useDefaults) {
2737 reset($contactDefaults['addressee']);
9bb77b8e
TO
2738 $addresseeID = key($contactDefaults['addressee']);
2739 $addresseeString = $contactDefaults['addressee'][$addresseeID];
6a488035
TO
2740 $updateQueryString[] = " addressee_id = $addresseeID ";
2741 $updateQueryString[] = " addressee_custom = NULL ";
2742 }
2743 elseif ($contact->addressee_custom) {
2744 $updateQueryString[] = " addressee_display = NULL ";
2745 }
2746 }
2747
2748 if ($addresseeString) {
73d64eb6 2749 CRM_Contact_BAO_Contact_Utils::processGreetingTemplate($addresseeString,
6a488035
TO
2750 $contactDetails,
2751 $contact->id,
2752 'CRM_Contact_BAO_Contact'
2753 );
2754 $addresseeString = CRM_Core_DAO::escapeString(CRM_Utils_String::stripSpaces($addresseeString));
2755 $updateQueryString[] = " addressee_display = '{$addresseeString}'";
2756 }
2757
2758 if (!empty($updateQueryString)) {
2759 $updateQueryString = implode(',', $updateQueryString);
2760 $queryString = "UPDATE civicrm_contact SET {$updateQueryString} WHERE id = {$contact->id}";
2761 CRM_Core_DAO::executeQuery($queryString);
2762 }
2763 }
2764
2765 /**
100fef9d 2766 * Retrieve loc block ids w/ given condition.
6a488035 2767 *
77c5b619
TO
2768 * @param int $contactId
2769 * Contact id.
2770 * @param array $criteria
2771 * Key => value pair which should be.
6a488035 2772 * fulfill by return record ids.
77c5b619
TO
2773 * @param string $condOperator
2774 * Operator use for grouping multiple conditions.
6a488035
TO
2775 *
2776 * @return array $locBlockIds loc block ids which fulfill condition.
2777 * @static
2778 */
00be9182 2779 public static function getLocBlockIds($contactId, $criteria = array(), $condOperator = 'AND') {
6a488035
TO
2780 $locBlockIds = array();
2781 if (!$contactId) {
2782 return $locBlockIds;
2783 }
2784
46a4f1e9 2785 foreach (array('Email', 'OpenID', 'Phone', 'Address', 'IM') as $block) {
6a488035 2786 $name = strtolower($block);
46a4f1e9
CW
2787 $className = "CRM_Core_DAO_$block";
2788 $blockDAO = new $className();
6a488035
TO
2789
2790 // build the condition.
2791 if (is_array($criteria)) {
46a4f1e9 2792 $fields = $blockDAO->fields();
6a488035
TO
2793 $conditions = array();
2794 foreach ($criteria as $field => $value) {
2795 if (array_key_exists($field, $fields)) {
2796 $cond = "( $field = $value )";
2797 // value might be zero or null.
2798 if (!$value || strtolower($value) == 'null') {
2799 $cond = "( $field = 0 OR $field IS NULL )";
2800 }
2801 $conditions[] = $cond;
2802 }
2803 }
2804 if (!empty($conditions)) {
2805 $blockDAO->whereAdd(implode(" $condOperator ", $conditions));
2806 }
2807 }
2808
2809 $blockDAO->contact_id = $contactId;
2810 $blockDAO->find();
2811 while ($blockDAO->fetch()) {
2812 $locBlockIds[$name][] = $blockDAO->id;
2813 }
2814 $blockDAO->free();
2815 }
2816
2817 return $locBlockIds;
2818 }
2819
2820 /**
100fef9d 2821 * Build context menu items.
6a488035 2822 *
100fef9d 2823 * @param int $contactId
77b97be7 2824 *
6a488035
TO
2825 * @return array of context menu for logged in user.
2826 * @static
2827 */
00be9182 2828 public static function contextMenu($contactId = NULL) {
6a488035 2829 $menu = array(
8e4ec1f5
CW
2830 'view' => array(
2831 'title' => ts('View Contact'),
6a488035
TO
2832 'weight' => 0,
2833 'ref' => 'view-contact',
8e4ec1f5 2834 'class' => 'no-popup',
6a488035
TO
2835 'key' => 'view',
2836 'permissions' => array('view all contacts'),
2837 ),
8e4ec1f5
CW
2838 'add' => array(
2839 'title' => ts('Edit Contact'),
6a488035
TO
2840 'weight' => 0,
2841 'ref' => 'edit-contact',
8e4ec1f5 2842 'class' => 'no-popup',
6a488035
TO
2843 'key' => 'add',
2844 'permissions' => array('edit all contacts'),
2845 ),
8e4ec1f5
CW
2846 'delete' => array(
2847 'title' => ts('Delete Contact'),
6a488035
TO
2848 'weight' => 0,
2849 'ref' => 'delete-contact',
2850 'key' => 'delete',
2851 'permissions' => array('access deleted contacts', 'delete contacts'),
2852 ),
8e4ec1f5
CW
2853 'contribution' => array(
2854 'title' => ts('Add Contribution'),
6a488035
TO
2855 'weight' => 5,
2856 'ref' => 'new-contribution',
8e4ec1f5
CW
2857 'key' => 'contribution',
2858 'tab' => 'contribute',
6a488035
TO
2859 'component' => 'CiviContribute',
2860 'href' => CRM_Utils_System::url('civicrm/contact/view/contribution',
2861 'reset=1&action=add&context=contribution'
2862 ),
2863 'permissions' => array(
2864 'access CiviContribute',
2865 'edit contributions',
2866 ),
2867 ),
8e4ec1f5
CW
2868 'participant' => array(
2869 'title' => ts('Register for Event'),
6a488035
TO
2870 'weight' => 10,
2871 'ref' => 'new-participant',
2872 'key' => 'participant',
8e4ec1f5 2873 'tab' => 'participant',
6a488035
TO
2874 'component' => 'CiviEvent',
2875 'href' => CRM_Utils_System::url('civicrm/contact/view/participant', 'reset=1&action=add&context=participant'),
2876 'permissions' => array(
2877 'access CiviEvent',
2878 'edit event participants',
2879 ),
2880 ),
8e4ec1f5
CW
2881 'activity' => array(
2882 'title' => ts('Record Activity'),
6a488035
TO
2883 'weight' => 35,
2884 'ref' => 'new-activity',
2885 'key' => 'activity',
2886 'permissions' => array('edit all contacts'),
2887 ),
8e4ec1f5
CW
2888 'pledge' => array(
2889 'title' => ts('Add Pledge'),
6a488035
TO
2890 'weight' => 15,
2891 'ref' => 'new-pledge',
2892 'key' => 'pledge',
8e4ec1f5 2893 'tab' => 'pledge',
6a488035
TO
2894 'href' => CRM_Utils_System::url('civicrm/contact/view/pledge',
2895 'reset=1&action=add&context=pledge'
2896 ),
2897 'component' => 'CiviPledge',
2898 'permissions' => array(
2899 'access CiviPledge',
2900 'edit pledges',
2901 ),
2902 ),
8e4ec1f5
CW
2903 'membership' => array(
2904 'title' => ts('Add Membership'),
6a488035
TO
2905 'weight' => 20,
2906 'ref' => 'new-membership',
8e4ec1f5
CW
2907 'key' => 'membership',
2908 'tab' => 'member',
6a488035
TO
2909 'component' => 'CiviMember',
2910 'href' => CRM_Utils_System::url('civicrm/contact/view/membership',
2911 'reset=1&action=add&context=membership'
2912 ),
2913 'permissions' => array(
2914 'access CiviMember',
2915 'edit memberships',
2916 ),
2917 ),
8e4ec1f5
CW
2918 'case' => array(
2919 'title' => ts('Add Case'),
6a488035
TO
2920 'weight' => 25,
2921 'ref' => 'new-case',
2922 'key' => 'case',
8e4ec1f5 2923 'tab' => 'case',
6a488035
TO
2924 'component' => 'CiviCase',
2925 'href' => CRM_Utils_System::url('civicrm/case/add', 'reset=1&action=add&context=case'),
2926 'permissions' => array('add cases'),
2927 ),
8e4ec1f5
CW
2928 'grant' => array(
2929 'title' => ts('Add Grant'),
6a488035
TO
2930 'weight' => 26,
2931 'ref' => 'new-grant',
2932 'key' => 'grant',
8e4ec1f5 2933 'tab' => 'grant',
6a488035
TO
2934 'component' => 'CiviGrant',
2935 'href' => CRM_Utils_System::url('civicrm/contact/view/grant',
2936 'reset=1&action=add&context=grant'
2937 ),
2938 'permissions' => array('edit grants'),
2939 ),
8e4ec1f5
CW
2940 'rel' => array(
2941 'title' => ts('Add Relationship'),
6a488035
TO
2942 'weight' => 30,
2943 'ref' => 'new-relationship',
2944 'key' => 'rel',
8e4ec1f5 2945 'tab' => 'rel',
6a488035
TO
2946 'href' => CRM_Utils_System::url('civicrm/contact/view/rel',
2947 'reset=1&action=add'
2948 ),
2949 'permissions' => array('edit all contacts'),
2950 ),
8e4ec1f5
CW
2951 'note' => array(
2952 'title' => ts('Add Note'),
6a488035
TO
2953 'weight' => 40,
2954 'ref' => 'new-note',
2955 'key' => 'note',
8e4ec1f5 2956 'tab' => 'note',
77042884 2957 'class' => 'medium-popup',
6a488035
TO
2958 'href' => CRM_Utils_System::url('civicrm/contact/view/note',
2959 'reset=1&action=add'
2960 ),
2961 'permissions' => array('edit all contacts'),
2962 ),
8e4ec1f5
CW
2963 'email' => array(
2964 'title' => ts('Send an Email'),
6a488035
TO
2965 'weight' => 45,
2966 'ref' => 'new-email',
2967 'key' => 'email',
2968 'permissions' => array('view all contacts'),
2969 ),
8e4ec1f5
CW
2970 'group' => array(
2971 'title' => ts('Add to Group'),
6a488035
TO
2972 'weight' => 50,
2973 'ref' => 'group-add-contact',
2974 'key' => 'group',
8e4ec1f5 2975 'tab' => 'group',
6a488035
TO
2976 'permissions' => array('edit groups'),
2977 ),
8e4ec1f5
CW
2978 'tag' => array(
2979 'title' => ts('Tag Contact'),
6a488035
TO
2980 'weight' => 55,
2981 'ref' => 'tag-contact',
2982 'key' => 'tag',
8e4ec1f5 2983 'tab' => 'tag',
6a488035
TO
2984 'permissions' => array('edit all contacts'),
2985 ),
2986 );
2987
2988 CRM_Utils_Hook::summaryActions($menu, $contactId);
2989 //1. check for component is active.
2990 //2. check for user permissions.
2991 //3. check for acls.
2992 //3. edit and view contact are directly accessible to user.
2993
2994 $aclPermissionedTasks = array(
9bb77b8e
TO
2995 'view-contact',
2996 'edit-contact',
2997 'new-activity',
2998 'new-email',
2999 'group-add-contact',
3000 'tag-contact',
3001 'delete-contact',
6a488035
TO
3002 );
3003 $corePermission = CRM_Core_Permission::getPermission();
3004
3005 $config = CRM_Core_Config::singleton();
3006
3007 $contextMenu = array();
3008 foreach ($menu as $key => $values) {
3009 $componentName = CRM_Utils_Array::value('component', $values);
3010
3011 // if component action - make sure component is enable.
3012 if ($componentName && !in_array($componentName, $config->enableComponents)) {
3013 continue;
3014 }
3015
3016 // make sure user has all required permissions.
3017 $hasAllPermissions = FALSE;
3018
3019 $permissions = CRM_Utils_Array::value('permissions', $values);
3020 if (!is_array($permissions) || empty($permissions)) {
3021 $hasAllPermissions = TRUE;
3022 }
3023
3024 // iterate for required permissions in given permissions array.
3025 if (!$hasAllPermissions) {
3026 $hasPermissions = 0;
3027 foreach ($permissions as $permission) {
3028 if (CRM_Core_Permission::check($permission)) {
3029 $hasPermissions++;
3030 }
3031 }
3032
3033 if (count($permissions) == $hasPermissions) {
3034 $hasAllPermissions = TRUE;
3035 }
3036
3037 // if still user does not have required permissions, check acl.
3038 if (!$hasAllPermissions && $values['ref'] != 'delete-contact') {
3039 if (in_array($values['ref'], $aclPermissionedTasks) &&
3040 $corePermission == CRM_Core_Permission::EDIT
3041 ) {
3042 $hasAllPermissions = TRUE;
3043 }
3044 elseif (in_array($values['ref'], array(
9bb77b8e
TO
3045 'new-email'
3046 ))) {
6a488035
TO
3047 // grant permissions for these tasks.
3048 $hasAllPermissions = TRUE;
3049 }
3050 }
3051 }
3052
3053 // user does not have necessary permissions.
3054 if (!$hasAllPermissions) {
3055 continue;
3056 }
3057
3058 // build directly accessible action menu.
3059 if (in_array($values['ref'], array(
9bb77b8e
TO
3060 'view-contact',
3061 'edit-contact'
3062 ))) {
6a488035
TO
3063 $contextMenu['primaryActions'][$key] = array(
3064 'title' => $values['title'],
3065 'ref' => $values['ref'],
8e4ec1f5 3066 'class' => CRM_Utils_Array::value('class', $values),
6a488035
TO
3067 'key' => $values['key'],
3068 );
3069 continue;
3070 }
3071
3072 // finally get menu item for -more- action widget.
3073 $contextMenu['moreActions'][$values['weight']] = array(
3074 'title' => $values['title'],
3075 'ref' => $values['ref'],
3076 'href' => CRM_Utils_Array::value('href', $values),
8e4ec1f5
CW
3077 'tab' => CRM_Utils_Array::value('tab', $values),
3078 'class' => CRM_Utils_Array::value('class', $values),
6a488035
TO
3079 'key' => $values['key'],
3080 );
3081 }
3082
3083 ksort($contextMenu['moreActions']);
3084
3085 return $contextMenu;
3086 }
3087
3088 /**
100fef9d 3089 * Retrieve display name of contact that address is shared
6a488035
TO
3090 * based on $masterAddressId or $contactId .
3091 *
77c5b619
TO
3092 * @param int $masterAddressId
3093 * Master id.
3094 * @param int $contactId
3095 * Contact id.
6a488035
TO
3096 *
3097 * @return display name |null the found display name or null.
6a488035
TO
3098 * @static
3099 */
00be9182 3100 public static function getMasterDisplayName($masterAddressId = NULL, $contactId = NULL) {
6a488035
TO
3101 $masterDisplayName = NULL;
3102 $sql = NULL;
3103 if (!$masterAddressId && !$contactId) {
3104 return $masterDisplayName;
3105 }
3106
3107 if ($masterAddressId) {
3108 $sql = "
3109 SELECT display_name from civicrm_contact
3110LEFT JOIN civicrm_address ON ( civicrm_address.contact_id = civicrm_contact.id )
3111 WHERE civicrm_address.id = " . $masterAddressId;
3112 }
3113 elseif ($contactId) {
3114 $sql = "
3115 SELECT display_name from civicrm_contact cc, civicrm_address add1
3116LEFT JOIN civicrm_address add2 ON ( add1.master_id = add2.id )
3117 WHERE cc.id = add2.contact_id AND add1.contact_id = " . $contactId;
3118 }
3119
3120 $masterDisplayName = CRM_Core_DAO::singleValueQuery($sql);
3121 return $masterDisplayName;
3122 }
3123
3124 /**
3125 * Get the creation/modification times for a contact
3126 *
100fef9d 3127 * @param int $contactId
77b97be7 3128 *
6a488035 3129 * @return array('created_date' => $, 'modified_date' => $)
77b97be7 3130 */
00be9182 3131 public static function getTimestamps($contactId) {
6a488035
TO
3132 $timestamps = CRM_Core_DAO::executeQuery(
3133 'SELECT created_date, modified_date
3134 FROM civicrm_contact
3135 WHERE id = %1',
3136 array(
3137 1 => array($contactId, 'Integer'),
3138 )
3139 );
3140 if ($timestamps->fetch()) {
3141 return array(
3142 'created_date' => $timestamps->created_date,
3143 'modified_date' => $timestamps->modified_date,
3144 );
3145 }
3146 else {
3147 return NULL;
3148 }
3149 }
3150
0590c631
DK
3151 /**
3152 * Generate triggers to update the timestamp on the corresponding civicrm_contact row,
3153 * on insert/update/delete to a table that extends civicrm_contact.
3154 * Don't regenerate triggers for all such tables if only asked for one table.
3155 *
3156 * @param array $info
3157 * Reference to the array where generated trigger information is being stored
3158 * @param string|null $reqTableName
3159 * Name of the table for which triggers are being generated, or NULL if all tables
3160 * @param array $relatedTableNames
3161 * Array of all core or all custom table names extending civicrm_contact
3162 * @param string $contactRefColumn
3163 * 'contact_id' if processing core tables, 'entity_id' if processing custom tables
3164 * @return void
3165 *
3166 * @link https://issues.civicrm.org/jira/browse/CRM-15602
3167 * @see triggerInfo
0590c631
DK
3168 * @static
3169 */
00be9182 3170 public static function generateTimestampTriggers(&$info, $reqTableName, $relatedTableNames, $contactRefColumn) {
0590c631
DK
3171 // Safety
3172 $contactRefColumn = CRM_Core_DAO::escapeString($contactRefColumn);
3173 // If specific related table requested, just process that one
3174 if (in_array($reqTableName, $relatedTableNames)) {
3175 $relatedTableNames = array($reqTableName);
3176 }
3177
3178 // If no specific table requested (include all related tables),
3179 // or a specific related table requested (as matched above)
3180 if (empty($reqTableName) || in_array($reqTableName, $relatedTableNames)) {
3181 $info[] = array(
3182 'table' => $relatedTableNames,
3183 'when' => 'AFTER',
3184 'event' => array('INSERT', 'UPDATE'),
3185 'sql' => "\nUPDATE civicrm_contact SET modified_date = CURRENT_TIMESTAMP WHERE id = NEW.$contactRefColumn;\n",
3186 );
3187 $info[] = array(
3188 'table' => $relatedTableNames,
3189 'when' => 'AFTER',
3190 'event' => array('DELETE'),
3191 'sql' => "\nUPDATE civicrm_contact SET modified_date = CURRENT_TIMESTAMP WHERE id = OLD.$contactRefColumn;\n",
3192 );
3193 }
3194 }
3195
6a488035
TO
3196 /**
3197 * Get a list of triggers for the contact table
3198 *
3199 * @see hook_civicrm_triggerInfo
3200 * @see CRM_Core_DAO::triggerRebuild
3201 * @see http://issues.civicrm.org/jira/browse/CRM-10554
3202 */
00be9182 3203 public static function triggerInfo(&$info, $tableName = NULL) {
6a488035
TO
3204 //during upgrade, first check for valid version and then create triggers
3205 //i.e the columns created_date and modified_date are introduced in 4.3.alpha1 so dont create triggers for older version
3206 if (CRM_Core_Config::isUpgradeMode()) {
3207 $currentVer = CRM_Core_BAO_Domain::version(TRUE);
3208 //if current version is less than 4.3.alpha1 dont create below triggers
3209 if (version_compare($currentVer, '4.3.alpha1') < 0) {
3210 return;
3211 }
3212 }
3213
0590c631 3214 // Update timestamp for civicrm_contact itself
6a488035
TO
3215 if ($tableName == NULL || $tableName == self::getTableName()) {
3216 $info[] = array(
3217 'table' => array(self::getTableName()),
3218 'when' => 'BEFORE',
3219 'event' => array('INSERT'),
3220 'sql' => "\nSET NEW.created_date = CURRENT_TIMESTAMP;\n",
3221 );
3222 }
3223
3224 // Update timestamp when modifying closely related core tables
3225 $relatedTables = array(
3226 'civicrm_address',
3227 'civicrm_email',
3228 'civicrm_im',
3229 'civicrm_phone',
3230 'civicrm_website',
3231 );
0590c631 3232 self::generateTimestampTriggers($info, $tableName, $relatedTables, 'contact_id');
6a488035
TO
3233
3234 // Update timestamp when modifying related custom-data tables
3235 $customGroupTables = array();
3236 $customGroupDAO = CRM_Core_BAO_CustomGroup::getAllCustomGroupsByBaseEntity('Contact');
3237 $customGroupDAO->is_multiple = 0;
3238 $customGroupDAO->find();
3239 while ($customGroupDAO->fetch()) {
3240 $customGroupTables[] = $customGroupDAO->table_name;
3241 }
3242 if (!empty($customGroupTables)) {
0590c631 3243 self::generateTimestampTriggers($info, $tableName, $customGroupTables, 'entity_id');
6a488035
TO
3244 }
3245
3246 // Update phone table to populate phone_numeric field
3247 if (!$tableName || $tableName == 'civicrm_phone') {
3248 // Define stored sql function needed for phones
3249 CRM_Core_DAO::executeQuery(self::DROP_STRIP_FUNCTION_43);
3250 CRM_Core_DAO::executeQuery(self::CREATE_STRIP_FUNCTION_43);
3251 $info[] = array(
3252 'table' => array('civicrm_phone'),
3253 'when' => 'BEFORE',
3254 'event' => array('INSERT', 'UPDATE'),
3255 'sql' => "\nSET NEW.phone_numeric = civicrm_strip_non_numeric(NEW.phone);\n",
3256 );
3257 }
3258 }
3259
3260 /**
100fef9d 3261 * Check if contact is being used in civicrm_domain
6a488035
TO
3262 * based on $contactId
3263 *
77c5b619
TO
3264 * @param int $contactId
3265 * Contact id.
6a488035 3266 *
e2046b33 3267 * @return bool true if present else false.
6a488035
TO
3268 * @static
3269 */
00be9182 3270 public static function checkDomainContact($contactId) {
6a488035
TO
3271 if (!$contactId)
3272 return FALSE;
9bb77b8e 3273 $domainId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Domain', $contactId, 'id', 'contact_id');
6a488035
TO
3274
3275 if ($domainId) {
3276 return TRUE;
9bb77b8e
TO
3277 }
3278 else {
6a488035
TO
3279 return FALSE;
3280 }
3281 }
12445e1c 3282
dc86f881
CW
3283 /**
3284 * Get options for a given contact field.
3285 * @see CRM_Core_DAO::buildOptions
3286 *
3287 * TODO: Should we always assume chainselect? What fn should be responsible for controlling that flow?
3288 * TODO: In context of chainselect, what to return if e.g. a country has no states?
3289 *
77c5b619
TO
3290 * @param string $fieldName
3291 * @param string $context
3292 * : @see CRM_Core_DAO::buildOptionsContext.
3293 * @param array $props
3294 * : whatever is known about this dao object.
77b97be7
EM
3295 *
3296 * @return Array|bool
dc86f881
CW
3297 */
3298 public static function buildOptions($fieldName, $context = NULL, $props = array()) {
3299 $params = array();
3300 // Special logic for fields whose options depend on context or properties
3301 switch ($fieldName) {
3302 case 'contact_sub_type':
3303 if (!empty($props['contact_type'])) {
3304 $params['condition'] = "parent_id = (SELECT id FROM civicrm_contact_type WHERE name='{$props['contact_type']}')";
3305 }
3306 break;
bee6039a
CW
3307 case 'contact_type':
3308 if ($context == 'search') {
76773c5a
CW
3309 // CRM-15495 - EntityRef filters and basic search forms expect this format
3310 // FIXME: Search builder does not
bee6039a
CW
3311 return CRM_Contact_BAO_ContactType::getSelectElements();
3312 }
3313 break;
dc86f881 3314 }
786ad6e1 3315 return CRM_Core_PseudoConstant::get(__CLASS__, $fieldName, $params, $context);
dc86f881 3316 }
1071730c 3317
12445e1c
CW
3318 /**
3319 * Delete a contact-related object that has an 'is_primary' field
3320 * Ensures that is_primary gets assigned to another object if available
3321 * Also calls pre/post hooks
3322 *
77b97be7
EM
3323 * @var $type : object type
3324 * @var $id : object id
3325 * @return bool
12445e1c
CW
3326 */
3327 public static function deleteObjectWithPrimary($type, $id) {
3328 if (!$id || !is_numeric($id)) {
a65e2e55 3329 return FALSE;
12445e1c
CW
3330 }
3331 $daoName = "CRM_Core_DAO_$type";
3332 $obj = new $daoName();
3333 $obj->id = $id;
3334 $obj->find();
3335 if ($obj->fetch()) {
3336 CRM_Utils_Hook::pre('delete', $type, $id, CRM_Core_DAO::$_nullArray);
3337 $contactId = $obj->contact_id;
3338 $obj->delete();
3339 }
3340 else {
a65e2e55 3341 return FALSE;
12445e1c 3342 }
50fa40e8
CW
3343 // is_primary is only relavent if this field belongs to a contact
3344 if ($contactId) {
3345 $dao = new $daoName();
3346 $dao->contact_id = $contactId;
3347 $dao->is_primary = 1;
3348 // Pick another record to be primary (if one isn't already)
3349 if (!$dao->find(TRUE)) {
3350 $dao->is_primary = 0;
3351 $dao->find();
3352 if ($dao->fetch()) {
3353 $dao->is_primary = 1;
3354 $dao->save();
3355 }
3356 }
3357 $dao->free();
12445e1c 3358 }
12445e1c
CW
3359 CRM_Utils_Hook::post('delete', $type, $id, $obj);
3360 $obj->free();
a65e2e55 3361 return TRUE;
12445e1c 3362 }
6a488035 3363}