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