Merge pull request #6178 from xurizaemon/CRM-16821
[civicrm-core.git] / CRM / Contact / BAO / Relationship.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
e7112fa7 6 | Copyright CiviCRM LLC (c) 2004-2015 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
bfa4da96 29 * Class CRM_Contact_BAO_Relationship.
6a488035
TO
30 */
31class CRM_Contact_BAO_Relationship extends CRM_Contact_DAO_Relationship {
32
33 /**
fe482240 34 * Various constants to indicate different type of relationships.
6a488035
TO
35 *
36 * @var int
37 */
a4dc03f7 38 const ALL = 0, PAST = 1, DISABLED = 2, CURRENT = 4, INACTIVE = 8;
6a488035 39
7ecf6275 40 /**
d49c2966 41 * Create function - use the API instead.
bfa4da96 42 *
2da59b29 43 * Note that the previous create function has been renamed 'legacyCreateMultiple'
7ecf6275 44 * and this is new in 4.6
2da59b29 45 * All existing calls have been changed to legacyCreateMultiple except the api call - however, it is recommended
bfa4da96
EM
46 * that you call that as the end to end testing here is based on the api & refactoring may still be done.
47 *
16b10e64 48 * @param array $params
bfa4da96 49 *
7ecf6275
EM
50 * @return \CRM_Contact_BAO_Relationship
51 * @throws \CRM_Core_Exception
52 */
53 public static function create(&$params) {
d49c2966 54 $params = self::loadExistingRelationshipDetails($params);
7ecf6275
EM
55 if (self::checkDuplicateRelationship($params, $params['contact_id_a'], $params['contact_id_b'], CRM_Utils_Array::value('id', $params, 0))) {
56 throw new CRM_Core_Exception('Duplicate Relationship');
57 }
58 if (self::checkValidRelationship($params, $params, 0)) {
936e43d6 59 throw new CRM_Core_Exception('Invalid Relationship');
7ecf6275
EM
60 }
61 $relationship = self::add($params);
936e43d6 62 if (!empty($params['contact_id_a'])) {
04ad3598
EM
63 $ids = array(
64 'contactTarget' => $relationship->contact_id_b,
65 'contact' => $params['contact_id_a'],
66 );
6c25ede2 67
690c56c3 68 //CRM-16087 removed additional call to function relatedMemberships which is already called by disableEnableRelationship
7a44a255 69 //resulting in membership being created twice
6946fcbf 70 if (array_key_exists('is_active', $params) && empty($params['is_active'])) {
71 $action = CRM_Core_Action::DISABLE;
72 $active = FALSE;
73 }
74 else {
75 $action = CRM_Core_Action::ENABLE;
76 $active = TRUE;
77 }
91244f1d 78 $id = empty($params['id']) ? $relationship->id : $params['id'];
79 self::disableEnableRelationship($id, $action, $params, $ids, $active);
6c25ede2 80 }
04ad3598 81
7ecf6275
EM
82 self::addRecent($params, $relationship);
83 return $relationship;
84 }
7f3647bc 85
2da59b29 86 /**
d49c2966 87 * Create multiple relationships for one contact.
2da59b29 88 *
d49c2966
EM
89 * The relationship details are the same for each relationship except the secondary contact
90 * id can be an array.
91 *
92 * @param array $params
93 * Parameters for creating multiple relationships.
94 * The parameters are the same as for relationship create function except that the non-primary
95 * end of the relationship should be an array of one or more contact IDs.
96 * @param string $primaryContactLetter
97 * a or b to denote the primary contact for this action. The secondary may be multiple contacts
98 * and should be an array.
2da59b29
EM
99 *
100 * @return array
101 * @throws \CRM_Core_Exception
102 */
103 public static function createMultiple($params, $primaryContactLetter) {
104 $secondaryContactLetter = ($primaryContactLetter == 'a') ? 'b' : 'a';
105 $secondaryContactIDs = $params['contact_id_' . $secondaryContactLetter];
106 $valid = $invalid = $duplicate = $saved = 0;
d49c2966 107 $relationshipIDs = array();
2da59b29
EM
108 foreach ($secondaryContactIDs as $secondaryContactID) {
109 try {
110 $params['contact_id_' . $secondaryContactLetter] = $secondaryContactID;
111 $relationship = civicrm_api3('relationship', 'create', $params);
d49c2966 112 $relationshipIDs[] = $relationship['id'];
bfa4da96 113 $valid++;
2da59b29
EM
114 }
115 catch (CiviCRM_API3_Exception $e) {
116 switch ($e->getMessage()) {
bfa4da96
EM
117 case 'Duplicate Relationship':
118 $duplicate++;
2da59b29
EM
119 break;
120
bfa4da96
EM
121 case 'Invalid Relationship':
122 $invalid++;
2da59b29
EM
123 break;
124
125 default:
126 throw new CRM_Core_Exception('unknown relationship create error ' . $e->getMessage());
127 }
128 }
129 }
130
d49c2966
EM
131 return array(
132 'valid' => $valid,
133 'invalid' => $invalid,
134 'duplicate' => $duplicate,
135 'saved' => $saved,
136 'relationship_ids' => $relationshipIDs,
137 );
2da59b29
EM
138 }
139
6a488035 140 /**
fe482240 141 * Takes an associative array and creates a relationship object.
7ecf6275
EM
142 * @deprecated For single creates use the api instead (it's tested).
143 * For multiple a new variant of this function needs to be written and migrated to as this is a bit
144 * nasty
6a488035 145 *
77c5b619
TO
146 * @param array $params
147 * (reference ) an assoc array of name/value pairs.
148 * @param array $ids
149 * The array that holds all the db ids.
16b10e64 150 * per http://wiki.civicrm.org/confluence/display/CRM/Database+layer
26dcc9eb 151 * "we are moving away from the $ids param "
6a488035 152 *
16b10e64 153 * @return CRM_Contact_BAO_Relationship
6a488035 154 */
2da59b29 155 public static function legacyCreateMultiple(&$params, $ids = array()) {
6a488035 156 $valid = $invalid = $duplicate = $saved = 0;
9af2925b 157 $relationships = $relationshipIds = array();
26dcc9eb 158 $relationshipId = CRM_Utils_Array::value('relationship', $ids, CRM_Utils_Array::value('id', $params));
5ad88e9d 159
6a488035
TO
160 //CRM-9015 - the hooks are called here & in add (since add doesn't call create)
161 // but in future should be tidied per ticket
9b873358 162 if (empty($relationshipId)) {
26dcc9eb 163 $hook = 'create';
6a488035 164 }
92e4c2a5 165 else {
26dcc9eb 166 $hook = 'edit';
6a488035 167 }
26dcc9eb 168
169 CRM_Utils_Hook::pre($hook, 'Relationship', $relationshipId, $params);
170
6a488035
TO
171 if (!$relationshipId) {
172 // creating a new relationship
173 $dataExists = self::dataExists($params);
174 if (!$dataExists) {
43291913 175 return array(FALSE, TRUE, FALSE, FALSE, NULL);
6a488035
TO
176 }
177 $relationshipIds = array();
178 foreach ($params['contact_check'] as $key => $value) {
6a488035
TO
179 // check if the relationship is valid between contacts.
180 // step 1: check if the relationship is valid if not valid skip and keep the count
181 // step 2: check the if two contacts already have a relationship if yes skip and keep the count
182 // step 3: if valid relationship then add the relation and keep the count
183
184 // step 1
2001204e
EM
185 $contactFields = self::setContactABFromIDs($params, $ids, $key);
186 $errors = self::checkValidRelationship($contactFields, $ids, $key);
6a488035
TO
187 if ($errors) {
188 $invalid++;
189 continue;
190 }
191
192 if (
7f3647bc 193 self::checkDuplicateRelationship(
2001204e 194 $contactFields,
7f3647bc
TO
195 CRM_Utils_Array::value('contact', $ids),
196 // step 2
197 $key
198 )
6a488035
TO
199 ) {
200 $duplicate++;
201 continue;
202 }
2001204e 203
bfa4da96 204 $singleInstanceParams = array_merge($params, $contactFields);
eff45dce 205 $relationship = self::add($singleInstanceParams);
6a488035 206 $relationshipIds[] = $relationship->id;
26dcc9eb 207 $relationships[$relationship->id] = $relationship;
6a488035
TO
208 $valid++;
209 }
210 // editing the relationship
211 }
212 else {
213 // check for duplicate relationship
f0fc26a5
DL
214 // @todo this code doesn't cope well with updates - causes e-Notices.
215 // API has a lot of code to work around
49f8272d 216 // this but should review this code & remove the extra handling from the api
f0fc26a5
DL
217 // it seems doubtful any of this is relevant if the contact fields & relationship
218 // type fields are not set
6a488035 219 if (
7f3647bc
TO
220 self::checkDuplicateRelationship(
221 $params,
222 CRM_Utils_Array::value('contact', $ids),
223 $ids['contactTarget'],
224 $relationshipId
225 )
6a488035
TO
226 ) {
227 $duplicate++;
43291913 228 return array($valid, $invalid, $duplicate, $saved, NULL);
6a488035
TO
229 }
230
231 $validContacts = TRUE;
232 //validate contacts in update mode also.
2f36ede8 233 $contactFields = self::setContactABFromIDs($params, $ids, $ids['contactTarget']);
8cc574cf 234 if (!empty($ids['contact']) && !empty($ids['contactTarget'])) {
2f36ede8 235 if (self::checkValidRelationship($contactFields, $ids, $ids['contactTarget'])) {
6a488035
TO
236 $validContacts = FALSE;
237 $invalid++;
238 }
239 }
240 if ($validContacts) {
241 // editing an existing relationship
2f36ede8
SB
242 $singleInstanceParams = array_merge($params, $contactFields);
243 $relationship = self::add($singleInstanceParams, $ids, $ids['contactTarget']);
6a488035 244 $relationshipIds[] = $relationship->id;
26dcc9eb 245 $relationships[$relationship->id] = $relationship;
6a488035
TO
246 $saved++;
247 }
248 }
249
250 // do not add to recent items for import, CRM-4399
8cc574cf 251 if (!(!empty($params['skipRecentView']) || $invalid || $duplicate)) {
7ecf6275 252 self::addRecent($params, $relationship);
6a488035
TO
253 }
254
26dcc9eb 255 return array($valid, $invalid, $duplicate, $saved, $relationshipIds, $relationships);
6a488035
TO
256 }
257
258 /**
bfa4da96 259 * This is the function that check/add if the relationship created is valid.
6a488035 260 *
77c5b619
TO
261 * @param array $params
262 * (reference ) an assoc array of name/value pairs.
77c5b619
TO
263 * @param array $ids
264 * The array that holds all the db ids.
c301f76e 265 * @param int $contactId
266 * This is contact id for adding relationship.
6a488035 267 *
c490a46a 268 * @return CRM_Contact_BAO_Relationship
6a488035 269 */
00be9182 270 public static function add(&$params, $ids = array(), $contactId = NULL) {
c301f76e 271 $relationshipId = CRM_Utils_Array::value('relationship', $ids, CRM_Utils_Array::value('id', $params));
f0fc26a5 272
49f8272d 273 $hook = 'create';
22e263ad 274 if ($relationshipId) {
49f8272d 275 $hook = 'edit';
6a488035 276 }
49f8272d 277 //@todo hook are called from create and add - remove one
7f3647bc 278 CRM_Utils_Hook::pre($hook, 'Relationship', $relationshipId, $params);
6a488035
TO
279
280 $relationshipTypes = CRM_Utils_Array::value('relationship_type_id', $params);
281
f0fc26a5
DL
282 // explode the string with _ to get the relationship type id
283 // and to know which contact has to be inserted in
6a488035 284 // contact_id_a and which one in contact_id_b
7ecf6275 285 list($type) = explode('_', $relationshipTypes);
6a488035 286
f0fc26a5
DL
287 // check if the relationship type is Head of Household then update the
288 // household's primary contact with this contact.
6a488035 289 if ($type == 6) {
7ecf6275 290 CRM_Contact_BAO_Household::updatePrimaryContact($params['contact_id_b'], $params['contact_id_a']);
6a488035
TO
291 }
292
293 $relationship = new CRM_Contact_BAO_Relationship();
49f8272d 294 //@todo this code needs to be updated for the possibility that not all fields are set
eff45dce 295 // by using $relationship->copyValues($params);
49f8272d 296 // (update)
7ecf6275
EM
297 $relationship->contact_id_b = $params['contact_id_b'];
298 $relationship->contact_id_a = $params['contact_id_a'];
6a488035 299 $relationship->relationship_type_id = $type;
49f8272d 300 $relationship->id = $relationshipId;
6a488035
TO
301
302 $dateFields = array('end_date', 'start_date');
303
9b873358
TO
304 foreach (self::getdefaults() as $defaultField => $defaultValue) {
305 if (isset($params[$defaultField])) {
306 if (in_array($defaultField, $dateFields)) {
6a488035 307 $relationship->$defaultField = CRM_Utils_Date::format(CRM_Utils_Array::value($defaultField, $params));
9b873358 308 if (!$relationship->$defaultField) {
6a488035
TO
309 $relationship->$defaultField = 'NULL';
310 }
311 }
92e4c2a5 312 else {
6a488035
TO
313 $relationship->$defaultField = $params[$defaultField];
314 }
315 }
7f3647bc 316 elseif (!$relationshipId) {
6a488035
TO
317 $relationship->$defaultField = $defaultValue;
318 }
319 }
320
321 $relationship->save();
322
323 // add custom field values
a7488080 324 if (!empty($params['custom'])) {
6a488035
TO
325 CRM_Core_BAO_CustomValueTable::store($params['custom'], 'civicrm_relationship', $relationship->id);
326 }
327
328 $relationship->free();
329
5c208c3f 330 CRM_Utils_Hook::post($hook, 'Relationship', $relationship->id, $relationship);
6a488035
TO
331
332 return $relationship;
333 }
7ecf6275
EM
334
335 /**
fe482240 336 * Add relationship to recent links.
bfa4da96 337 *
7ecf6275
EM
338 * @param array $params
339 * @param CRM_Contact_DAO_Relationship $relationship
340 */
341 public static function addRecent($params, $relationship) {
342 $url = CRM_Utils_System::url('civicrm/contact/view/rel',
343 "action=view&reset=1&id={$relationship->id}&cid={$relationship->contact_id_a}&context=home"
344 );
345 $session = CRM_Core_Session::singleton();
346 $recentOther = array();
347 if (($session->get('userID') == $relationship->contact_id_a) ||
348 CRM_Contact_BAO_Contact_Permission::allow($relationship->contact_id_a, CRM_Core_Permission::EDIT)
349 ) {
350 $rType = substr(CRM_Utils_Array::value('relationship_type_id', $params), -3);
351 $recentOther = array(
352 'editUrl' => CRM_Utils_System::url('civicrm/contact/view/rel',
353 "action=update&reset=1&id={$relationship->id}&cid={$relationship->contact_id_a}&rtype={$rType}&context=home"
354 ),
355 'deleteUrl' => CRM_Utils_System::url('civicrm/contact/view/rel',
356 "action=delete&reset=1&id={$relationship->id}&cid={$relationship->contact_id_a}&rtype={$rType}&context=home"
357 ),
358 );
359 }
360 $title = CRM_Contact_BAO_Contact::displayName($relationship->contact_id_a) . ' (' . CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType',
361 $relationship->relationship_type_id, 'label_a_b'
362 ) . ' ' . CRM_Contact_BAO_Contact::displayName($relationship->contact_id_b) . ')';
363
364 CRM_Utils_Recent::add($title,
365 $url,
366 $relationship->id,
367 'Relationship',
368 $relationship->contact_id_a,
369 NULL,
370 $recentOther
371 );
372 }
373
d49c2966
EM
374 /**
375 * Load contact ids and relationship type id when doing a create call if not provided.
376 *
377 * There are are various checks done in create which require this information which is optional
378 * when using id.
379 *
380 * @param array $params
381 * Parameters passed to create call.
382 *
383 * @return array
384 * Parameters with missing fields added if required.
385 */
386 public static function loadExistingRelationshipDetails($params) {
387 if (!empty($params['contact_id_a'])
388 && !empty($params['contact_id_b'])
389 && is_numeric($params['relationship_type_id'])) {
390 return $params;
391 }
392 if (empty($params['id'])) {
393 return $params;
394 }
395
396 $fieldsToFill = array('contact_id_a', 'contact_id_b', 'relationship_type_id');
397 $result = CRM_Core_DAO::executeQuery("SELECT " . implode(',', $fieldsToFill) . " FROM civicrm_relationship WHERE id = %1", array(
398 1 => array(
399 $params['id'],
400 'Integer',
401 ),
402 ));
403 while ($result->fetch()) {
404 foreach ($fieldsToFill as $field) {
405 $params[$field] = !empty($params[$field]) ? $params[$field] : $result->$field;
406 }
407 }
408 return $params;
409 }
410
7ecf6275 411 /**
bfa4da96 412 * Resolve passed in contact IDs to contact_id_a & contact_id_b.
eff45dce 413 *
16b10e64 414 * @param array $params
7ecf6275
EM
415 * @param array $ids
416 * @param null $contactID
eff45dce
EM
417 *
418 * @return array
7ecf6275
EM
419 * @throws \CRM_Core_Exception
420 */
eff45dce
EM
421 public static function setContactABFromIDs($params, $ids = array(), $contactID = NULL) {
422 $returnFields = array();
d49c2966
EM
423
424 // $ids['contact'] is deprecated but comes from legacyCreateMultiple function.
7ecf6275
EM
425 if (empty($ids['contact'])) {
426 if (!empty($params['id'])) {
d49c2966 427 return self::loadExistingRelationshipDetails($params);
7ecf6275
EM
428 }
429 throw new CRM_Core_Exception('Cannot create relationship, insufficient contact IDs provided');
430 }
2001204e 431 if (isset($params['relationship_type_id']) && !is_numeric($params['relationship_type_id'])) {
eff45dce
EM
432 $relationshipTypes = CRM_Utils_Array::value('relationship_type_id', $params);
433 list($relationshipTypeID, $first) = explode('_', $relationshipTypes);
2001204e
EM
434 $returnFields['relationship_type_id'] = $relationshipTypeID;
435
d49c2966
EM
436 foreach (array('a', 'b') as $contactLetter) {
437 if (empty($params['contact_' . $contactLetter])) {
438 if ($first == $contactLetter) {
439 $returnFields['contact_id_' . $contactLetter] = CRM_Utils_Array::value('contact', $ids);
440 }
441 else {
442 $returnFields['contact_id_' . $contactLetter] = $contactID;
443 }
7ecf6275
EM
444 }
445 }
446 }
d49c2966 447
eff45dce 448 return $returnFields;
7ecf6275
EM
449 }
450
6a488035 451 /**
d49c2966 452 * Specify defaults for creating a relationship.
6a488035 453 *
a6c01b45
CW
454 * @return array
455 * array of defaults for creating relationship
6a488035 456 */
00be9182 457 public static function getdefaults() {
6a488035
TO
458 return array(
459 'is_active' => 0,
460 'is_permission_a_b' => 0,
461 'is_permission_b_a' => 0,
462 'description' => '',
463 'start_date' => 'NULL',
464 'case_id' => NULL,
465 'end_date' => 'NULL',
466 );
467 }
468
469
470 /**
fe482240 471 * Check if there is data to create the object.
6a488035 472 *
77c5b619
TO
473 * @param array $params
474 * (reference ) an assoc array of name/value pairs.
6a488035 475 *
c301f76e 476 * @return bool
6a488035 477 */
00be9182 478 public static function dataExists(&$params) {
6a488035
TO
479 // return if no data present
480 if (!is_array(CRM_Utils_Array::value('contact_check', $params))) {
481 return FALSE;
482 }
483 return TRUE;
484 }
485
486 /**
100fef9d 487 * Get get list of relationship type based on the contact type.
6a488035 488 *
77c5b619
TO
489 * @param int $contactId
490 * This is the contact id of the current contact.
fd31fa4c 491 * @param null $contactSuffix
77c5b619
TO
492 * @param string $relationshipId
493 * The id of the existing relationship if any.
494 * @param string $contactType
495 * Contact type.
496 * @param bool $all
497 * If true returns relationship types in both the direction.
498 * @param string $column
499 * Name/label that going to retrieve from db.
fd31fa4c 500 * @param bool $biDirectional
77c5b619 501 * @param string $contactSubType
bfa4da96 502 * Includes relationship types between this subtype.
77c5b619 503 * @param bool $onlySubTypeRelationTypes
bfa4da96
EM
504 * If set only subtype which is passed by $contactSubType
505 * related relationship types get return
6a488035 506 *
a6c01b45
CW
507 * @return array
508 * array reference of all relationship types with context to current contact.
6a488035 509 */
c301f76e 510 public static function getContactRelationshipType(
51ccfbbe 511 $contactId = NULL,
6a488035
TO
512 $contactSuffix = NULL,
513 $relationshipId = NULL,
514 $contactType = NULL,
515 $all = FALSE,
516 $column = 'label',
517 $biDirectional = TRUE,
518 $contactSubType = NULL,
519 $onlySubTypeRelationTypes = FALSE
520 ) {
d49c2966 521
7f3647bc 522 $relationshipType = array();
6a488035
TO
523 $allRelationshipType = CRM_Core_PseudoConstant::relationshipType($column);
524
525 $otherContactType = NULL;
526 if ($relationshipId) {
527 $relationship = new CRM_Contact_DAO_Relationship();
528 $relationship->id = $relationshipId;
529 if ($relationship->find(TRUE)) {
530 $contact = new CRM_Contact_DAO_Contact();
531 $contact->id = ($relationship->contact_id_a === $contactId) ? $relationship->contact_id_b : $relationship->contact_id_a;
532
533 if ($contact->find(TRUE)) {
534 $otherContactType = $contact->contact_type;
535 //CRM-5125 for contact subtype specific relationshiptypes
536 if ($contact->contact_sub_type) {
537 $otherContactSubType = $contact->contact_sub_type;
538 }
539 }
540 }
541 }
542
543 $contactSubType = array();
544 if ($contactId) {
545 $contactType = CRM_Contact_BAO_Contact::getContactType($contactId);
546 $contactSubType = CRM_Contact_BAO_Contact::getContactSubType($contactId);
547 }
548
549 foreach ($allRelationshipType as $key => $value) {
550 // the contact type is required or matches
551 if (((!$value['contact_type_a']) ||
552 $value['contact_type_a'] == $contactType
553 ) &&
554 // the other contact type is required or present or matches
555 ((!$value['contact_type_b']) ||
556 (!$otherContactType) ||
557 $value['contact_type_b'] == $otherContactType
558 ) &&
91c0d0d8 559 (in_array($value['contact_sub_type_a'], $contactSubType) ||
560 in_array($value['contact_sub_type_b'], $contactSubType) ||
561 (!$value['contact_sub_type_a'] && !$onlySubTypeRelationTypes)
6a488035
TO
562 )
563 ) {
564 $relationshipType[$key . '_a_b'] = $value["{$column}_a_b"];
565 }
6a488035
TO
566
567 if (((!$value['contact_type_b']) ||
568 $value['contact_type_b'] == $contactType
569 ) &&
570 ((!$value['contact_type_a']) ||
571 (!$otherContactType) ||
572 $value['contact_type_a'] == $otherContactType
573 ) &&
91c0d0d8 574 (in_array($value['contact_sub_type_b'], $contactSubType) ||
575 in_array($value['contact_sub_type_a'], $contactSubType) ||
576 (!$value['contact_sub_type_b'] && !$onlySubTypeRelationTypes)
6a488035
TO
577 )
578 ) {
579 $relationshipType[$key . '_b_a'] = $value["{$column}_b_a"];
580 }
6a488035
TO
581
582 if ($all) {
583 $relationshipType[$key . '_a_b'] = $value["{$column}_a_b"];
584 $relationshipType[$key . '_b_a'] = $value["{$column}_b_a"];
585 }
586 }
587
588 if ($biDirectional) {
589 // lets clean up the data and eliminate all duplicate values
590 // (i.e. the relationship is bi-directional)
591 $relationshipType = array_unique($relationshipType);
592 }
593
594 // sort the relationshipType in ascending order CRM-7736
595 asort($relationshipType);
596 return $relationshipType;
597 }
598
86538308 599 /**
98a06ae0
EM
600 * Delete current employer relationship.
601 *
100fef9d 602 * @param int $id
98a06ae0 603 * @param int $action
86538308
EM
604 *
605 * @return CRM_Contact_DAO_Relationship
606 */
00be9182 607 public static function clearCurrentEmployer($id, $action) {
6a488035
TO
608 $relationship = new CRM_Contact_DAO_Relationship();
609 $relationship->id = $id;
610 $relationship->find(TRUE);
611
612 //to delete relationship between household and individual \
98a06ae0 613 //or between individual and organization
6a488035 614 if (($action & CRM_Core_Action::DISABLE) || ($action & CRM_Core_Action::DELETE)) {
05802b8e
DG
615 $relTypes = CRM_Utils_Array::index(array('name_a_b'), CRM_Core_PseudoConstant::relationshipType('name'));
616 if ($relationship->relationship_type_id == $relTypes['Employee of']['id'] ||
7f3647bc
TO
617 $relationship->relationship_type_id == $relTypes['Household Member of']['id']
618 ) {
05802b8e
DG
619 $sharedContact = new CRM_Contact_DAO_Contact();
620 $sharedContact->id = $relationship->contact_id_a;
621 $sharedContact->find(TRUE);
622
bd655ee6
MV
623 // CRM-15881 UPDATES
624 // changed FROM "...relationship->relationship_type_id == 4..." TO "...relationship->relationship_type_id == 5..."
625 // As the system should be looking for type "employer of" (id 5) and not "sibling of" (id 4)
3421dcee
MV
626 // As suggested by @davecivicrm, the employee relationship type id is fetched using the CRM_Core_DAO::getFieldValue() class and method, since these ids differ from system to system.
627 $employerRelTypeId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', 'Employee of', 'id', 'name_a_b');
628
629 if ($relationship->relationship_type_id == $employerRelTypeId && $relationship->contact_id_b == $sharedContact->employer_id) {
05802b8e
DG
630 CRM_Contact_BAO_Contact_Utils::clearCurrentEmployer($relationship->contact_id_a);
631 }
3421dcee 632
6a488035
TO
633 }
634 }
7f3647bc 635 return $relationship;
6a488035
TO
636 }
637
638 /**
fe482240 639 * Delete the relationship.
6a488035 640 *
77c5b619
TO
641 * @param int $id
642 * Relationship id.
6a488035
TO
643 *
644 * @return null
6a488035 645 */
00be9182 646 public static function del($id) {
6a488035
TO
647 // delete from relationship table
648 CRM_Utils_Hook::pre('delete', 'Relationship', $id, CRM_Core_DAO::$_nullArray);
649
650 $relationship = self::clearCurrentEmployer($id, CRM_Core_Action::DELETE);
651 if (CRM_Core_Permission::access('CiviMember')) {
652 // create $params array which isrequired to delete memberships
653 // of the related contacts.
654 $params = array(
655 'relationship_type_id' => "{$relationship->relationship_type_id}_a_b",
656 'contact_check' => array($relationship->contact_id_b => 1),
657 );
658
659 $ids = array();
660 // calling relatedMemberships to delete the memberships of
661 // related contacts.
662 self::relatedMemberships($relationship->contact_id_a,
663 $params,
664 $ids,
665 CRM_Core_Action::DELETE,
666 FALSE
667 );
668 }
669
670 $relationship->delete();
671 CRM_Core_Session::setStatus(ts('Selected relationship has been deleted successfully.'), ts('Record Deleted'), 'success');
672
5c208c3f 673 CRM_Utils_Hook::post('delete', 'Relationship', $id, $relationship);
6a488035
TO
674
675 // delete the recently created Relationship
676 $relationshipRecent = array(
677 'id' => $id,
678 'type' => 'Relationship',
679 );
680 CRM_Utils_Recent::del($relationshipRecent);
681
682 return $relationship;
683 }
684
685 /**
98a06ae0 686 * Disable/enable the relationship.
6a488035 687 *
77c5b619
TO
688 * @param int $id
689 * Relationship id.
6a488035 690 *
7a44a255
EM
691 * @param int $action
692 * @param array $params
693 * @param array $ids
694 * @param bool $active
6a488035 695 */
690c56c3 696 public static function disableEnableRelationship($id, $action, $params = array(), $ids = array(), $active = FALSE) {
6a488035 697 $relationship = self::clearCurrentEmployer($id, $action);
25f1372e 698
6a488035 699 if (CRM_Core_Permission::access('CiviMember')) {
25f1372e 700 // create $params array which is required to delete memberships
6a488035 701 // of the related contacts.
690c56c3 702 if (empty($params)) {
703 $params = array(
704 'relationship_type_id' => "{$relationship->relationship_type_id}_a_b",
705 'contact_check' => array($relationship->contact_id_b => 1),
706 );
707 }
690c56c3 708 $contact_id_a = empty($params['contact_id_a']) ? $relationship->contact_id_a : $params['contact_id_a'];
6a488035
TO
709 // calling relatedMemberships to delete/add the memberships of
710 // related contacts.
711 if ($action & CRM_Core_Action::DISABLE) {
690c56c3 712 CRM_Contact_BAO_Relationship::relatedMemberships($contact_id_a,
6a488035
TO
713 $params,
714 $ids,
715 CRM_Core_Action::DELETE,
690c56c3 716 $active
6a488035
TO
717 );
718 }
719 elseif ($action & CRM_Core_Action::ENABLE) {
91244f1d 720 $ids['contact'] = empty($ids['contact']) ? $contact_id_a : $ids['contact'];
690c56c3 721 CRM_Contact_BAO_Relationship::relatedMemberships($contact_id_a,
6a488035
TO
722 $params,
723 $ids,
690c56c3 724 empty($params['id']) ? CRM_Core_Action::ADD : CRM_Core_Action::UPDATE,
725 $active
6a488035
TO
726 );
727 }
728 }
729 }
730
731 /**
fe482240 732 * Delete the object records that are associated with this contact.
6a488035 733 *
77c5b619
TO
734 * @param int $contactId
735 * Id of the contact to delete.
6a488035 736 */
00be9182 737 public static function deleteContact($contactId) {
6a488035
TO
738 $relationship = new CRM_Contact_DAO_Relationship();
739 $relationship->contact_id_a = $contactId;
740 $relationship->delete();
741
742 $relationship = new CRM_Contact_DAO_Relationship();
743 $relationship->contact_id_b = $contactId;
744 $relationship->delete();
745
746 CRM_Contact_BAO_Household::updatePrimaryContact(NULL, $contactId);
747 }
748
749 /**
fe482240 750 * Get the other contact in a relationship.
6a488035 751 *
77c5b619
TO
752 * @param int $id
753 * Relationship id.
6a488035
TO
754 *
755 * $returns returns the contact ids in the realtionship
77b97be7
EM
756 *
757 * @return \CRM_Contact_DAO_Relationship
6a488035 758 */
eff45dce 759 public static function getRelationshipByID($id) {
6a488035
TO
760 $relationship = new CRM_Contact_DAO_Relationship();
761
762 $relationship->id = $id;
763 $relationship->selectAdd();
764 $relationship->selectAdd('contact_id_a, contact_id_b');
765 $relationship->find(TRUE);
766
767 return $relationship;
768 }
769
770 /**
fe482240 771 * Check if the relationship type selected between two contacts is correct.
6a488035 772 *
77c5b619
TO
773 * @param int $contact_a
774 * 1st contact id.
775 * @param int $contact_b
776 * 2nd contact id.
777 * @param int $relationshipTypeId
778 * Relationship type id.
6a488035 779 *
c301f76e 780 * @return bool
a6c01b45 781 * true if it is valid relationship else false
6a488035 782 */
00be9182 783 public static function checkRelationshipType($contact_a, $contact_b, $relationshipTypeId) {
6a488035
TO
784 $relationshipType = new CRM_Contact_DAO_RelationshipType();
785 $relationshipType->id = $relationshipTypeId;
786 $relationshipType->selectAdd();
787 $relationshipType->selectAdd('contact_type_a, contact_type_b, contact_sub_type_a, contact_sub_type_b');
788 if ($relationshipType->find(TRUE)) {
789 $contact_type_a = CRM_Contact_BAO_Contact::getContactType($contact_a);
790 $contact_type_b = CRM_Contact_BAO_Contact::getContactType($contact_b);
791
792 $contact_sub_type_a = CRM_Contact_BAO_Contact::getContactSubType($contact_a);
793 $contact_sub_type_b = CRM_Contact_BAO_Contact::getContactSubType($contact_b);
794
795 if (((!$relationshipType->contact_type_a) || ($relationshipType->contact_type_a == $contact_type_a)) &&
796 ((!$relationshipType->contact_type_b) || ($relationshipType->contact_type_b == $contact_type_b)) &&
797 ((!$relationshipType->contact_sub_type_a) || (in_array($relationshipType->contact_sub_type_a,
7f3647bc
TO
798 $contact_sub_type_a
799 ))) &&
6a488035 800 ((!$relationshipType->contact_sub_type_b) || (in_array($relationshipType->contact_sub_type_b,
7f3647bc
TO
801 $contact_sub_type_b
802 )))
6a488035
TO
803 ) {
804 return TRUE;
805 }
806 else {
807 return FALSE;
808 }
809 }
810 return FALSE;
811 }
812
813 /**
fe482240 814 * This function does the validtion for valid relationship.
6a488035 815 *
77c5b619
TO
816 * @param array $params
817 * This array contains the values there are subitted by the form.
818 * @param array $ids
819 * The array that holds all the db ids.
820 * @param int $contactId
821 * This is contact id for adding relationship.
6a488035 822 *
2a6da8d7 823 * @return string
6a488035 824 */
7ecf6275 825 public static function checkValidRelationship($params, $ids, $contactId) {
6a488035 826 $errors = '';
6a488035
TO
827 // function to check if the relationship selected is correct
828 // i.e. employer relationship can exit between Individual and Organization (not between Individual and Individual)
c4dcea5b
EM
829 if (!CRM_Contact_BAO_Relationship::checkRelationshipType($params['contact_id_a'], $params['contact_id_b'],
830 $params['relationship_type_id'])) {
6a488035
TO
831 $errors = 'Please select valid relationship between these two contacts.';
832 }
833 return $errors;
834 }
835
836 /**
fe482240 837 * This function checks for duplicate relationship.
6a488035 838 *
77c5b619
TO
839 * @param array $params
840 * (reference ) an assoc array of name/value pairs.
841 * @param int $id
842 * This the id of the contact whom we are adding relationship.
843 * @param int $contactId
844 * This is contact id for adding relationship.
845 * @param int $relationshipId
846 * This is relationship id for the contact.
6a488035 847 *
c301f76e 848 * @return bool
a6c01b45 849 * true if record exists else false
6a488035 850 */
00be9182 851 public static function checkDuplicateRelationship(&$params, $id, $contactId = 0, $relationshipId = 0) {
6a488035 852 $relationshipTypeId = CRM_Utils_Array::value('relationship_type_id', $params);
decc60a0 853 list($type) = explode('_', $relationshipTypeId);
6a488035 854
f0fc26a5
DL
855 $queryString = "
856SELECT id
857FROM civicrm_relationship
858WHERE relationship_type_id = " . CRM_Utils_Type::escape($type, 'Integer');
6a488035
TO
859
860 /*
e70a7fc0
TO
861 * CRM-11792 - date fields from API are in ISO format, but this function
862 * supports date arrays BAO has increasingly standardised to ISO format
863 * so I believe this function should support ISO rather than make API
864 * format it - however, need to support array format for now to avoid breakage
2da59b29 865 * @ time of writing this function is called from Relationship::legacyCreateMultiple (twice)
e70a7fc0
TO
866 * CRM_BAO_Contact_Utils::clearCurrentEmployer (seemingly without dates)
867 * CRM_Contact_Form_Task_AddToOrganization::postProcess &
868 * CRM_Contact_Form_Task_AddToHousehold::postProcess
869 * (I don't think the last 2 support dates but not sure
870 */
6a488035
TO
871
872 $dateFields = array('end_date', 'start_date');
9b873358 873 foreach ($dateFields as $dateField) {
22e263ad 874 if (array_key_exists($dateField, $params)) {
9b873358 875 if (empty($params[$dateField]) || $params[$dateField] == 'null') {
f0fc26a5
DL
876 //this is most likely coming from an api call & probably loaded
877 // from the DB to deal with some of the
878 // other myriad of excessive checks still in place both in
879 // the api & the create functions
6a488035
TO
880 $queryString .= " AND $dateField IS NULL";
881 continue;
882 }
9b873358 883 elseif (is_array($params[$dateField])) {
f0fc26a5
DL
884 $queryString .= " AND $dateField = " .
885 CRM_Utils_Type::escape(CRM_Utils_Date::format($params[$dateField]), 'Date');
6a488035 886 }
f0fc26a5
DL
887 else {
888 $queryString .= " AND $dateField = " .
889 CRM_Utils_Type::escape($params[$dateField], 'Date');
6a488035
TO
890 }
891 }
892 }
893
894 $queryString .=
895 " AND ( ( contact_id_a = " . CRM_Utils_Type::escape($id, 'Integer') .
896 " AND contact_id_b = " . CRM_Utils_Type::escape($contactId, 'Integer') .
897 " ) OR ( contact_id_a = " . CRM_Utils_Type::escape($contactId, 'Integer') .
898 " AND contact_id_b = " . CRM_Utils_Type::escape($id, 'Integer') . " ) ) ";
899
900 //if caseId is provided, include it duplicate checking.
901 if ($caseId = CRM_Utils_Array::value('case_id', $params)) {
902 $queryString .= " AND case_id = " . CRM_Utils_Type::escape($caseId, 'Integer');
903 }
904
905 if ($relationshipId) {
906 $queryString .= " AND id !=" . CRM_Utils_Type::escape($relationshipId, 'Integer');
907 }
908
909 $relationship = new CRM_Contact_BAO_Relationship();
910 $relationship->query($queryString);
911 $relationship->fetch();
912 $relationship->free();
913 return ($relationship->id) ? TRUE : FALSE;
914 }
915
916 /**
fe482240 917 * Update the is_active flag in the db.
6a488035 918 *
77c5b619
TO
919 * @param int $id
920 * Id of the database record.
921 * @param bool $is_active
922 * Value we want to set the is_active field.
6a488035 923 *
77b97be7 924 * @throws CiviCRM_API3_Exception
a6c01b45
CW
925 * @return Object
926 * DAO object on success, null otherwise
6a488035 927 */
00be9182 928 public static function setIsActive($id, $is_active) {
49f8272d
E
929 // as both the create & add functions have a bunch of logic in them that
930 // doesn't seem to cope with a normal update we will call the api which
931 // has tested handling for this
932 // however, a longer term solution would be to simplify the add, create & api functions
933 // to be more standard. It is debatable @ that point whether it's better to call the BAO
934 // direct as the api is more tested.
80109b10 935 $result = civicrm_api('relationship', 'create', array(
936 'id' => $id,
937 'is_active' => $is_active,
938 'version' => 3,
939 ));
940
941 if (is_array($result) && !empty($result['is_error']) && $result['error_message'] != 'Relationship already exists') {
942 throw new CiviCRM_API3_Exception($result['error_message'], CRM_Utils_Array::value('error_code', $result, 'undefined'), $result);
943 }
49f8272d 944
6a488035
TO
945 return TRUE;
946 }
947
948 /**
98a06ae0 949 * Fetch a relationship object and store the values in the values array.
6a488035 950 *
77c5b619
TO
951 * @param array $params
952 * Input parameters to find object.
953 * @param array $values
954 * Output values of the object.
6a488035 955 *
a6c01b45
CW
956 * @return array
957 * (reference) the values that could be potentially assigned to smarty
6a488035 958 */
00be9182 959 public static function &getValues(&$params, &$values) {
6a488035
TO
960 if (empty($params)) {
961 return NULL;
962 }
963 $v = array();
964
965 // get the specific number of relationship or all relationships.
a7488080 966 if (!empty($params['numRelationship'])) {
6a488035
TO
967 $v['data'] = &CRM_Contact_BAO_Relationship::getRelationship($params['contact_id'], NULL, $params['numRelationship']);
968 }
969 else {
970 $v['data'] = CRM_Contact_BAO_Relationship::getRelationship($params['contact_id']);
971 }
972
973 // get the total count of relationships
974 $v['totalCount'] = CRM_Contact_BAO_Relationship::getRelationship($params['contact_id'], NULL, NULL, TRUE);
975
976 $values['relationship']['data'] = &$v['data'];
977 $values['relationship']['totalCount'] = &$v['totalCount'];
978
979 return $v;
980 }
981
982 /**
fe482240 983 * Helper function to form the sql for relationship retrieval.
6a488035 984 *
77c5b619
TO
985 * @param int $contactId
986 * Contact id.
987 * @param int $status
988 * (check const at top of file).
989 * @param int $numRelationship
990 * No of relationships to display (limit).
991 * @param int $count
992 * Get the no of relationships.
6a488035 993 * $param int $relationshipId relationship id
100fef9d 994 * @param int $relationshipId
77c5b619
TO
995 * @param string $direction
996 * The direction we are interested in a_b or b_a.
997 * @param array $params
998 * Array of extra values including relationship_type_id per api spec.
6a488035 999 *
77b97be7 1000 * @return array
76e7a76c 1001 * [select, from, where]
6a488035 1002 */
00be9182 1003 public static function makeURLClause($contactId, $status, $numRelationship, $count, $relationshipId, $direction, $params = array()) {
6a488035
TO
1004 $select = $from = $where = '';
1005
1006 $select = '( ';
1007 if ($count) {
1008 if ($direction == 'a_b') {
1009 $select .= ' SELECT count(DISTINCT civicrm_relationship.id) as cnt1, 0 as cnt2 ';
1010 }
1011 else {
1012 $select .= ' SELECT 0 as cnt1, count(DISTINCT civicrm_relationship.id) as cnt2 ';
1013 }
1014 }
1015 else {
1016 $select .= ' SELECT civicrm_relationship.id as civicrm_relationship_id,
1017 civicrm_contact.sort_name as sort_name,
1018 civicrm_contact.display_name as display_name,
1019 civicrm_contact.job_title as job_title,
1020 civicrm_contact.employer_id as employer_id,
1021 civicrm_contact.organization_name as organization_name,
1022 civicrm_address.street_address as street_address,
1023 civicrm_address.city as city,
1024 civicrm_address.postal_code as postal_code,
1025 civicrm_state_province.abbreviation as state,
1026 civicrm_country.name as country,
1027 civicrm_email.email as email,
c6c691a5 1028 civicrm_contact.contact_type as contact_type,
6a488035
TO
1029 civicrm_phone.phone as phone,
1030 civicrm_contact.id as civicrm_contact_id,
6a488035
TO
1031 civicrm_relationship.contact_id_b as contact_id_b,
1032 civicrm_relationship.contact_id_a as contact_id_a,
1033 civicrm_relationship_type.id as civicrm_relationship_type_id,
1034 civicrm_relationship.start_date as start_date,
1035 civicrm_relationship.end_date as end_date,
1036 civicrm_relationship.description as description,
1037 civicrm_relationship.is_active as is_active,
1038 civicrm_relationship.is_permission_a_b as is_permission_a_b,
1039 civicrm_relationship.is_permission_b_a as is_permission_b_a,
1040 civicrm_relationship.case_id as case_id';
1041
1042 if ($direction == 'a_b') {
1043 $select .= ', civicrm_relationship_type.label_a_b as label_a_b,
c6c691a5 1044 civicrm_relationship_type.label_b_a as relation ';
6a488035
TO
1045 }
1046 else {
1047 $select .= ', civicrm_relationship_type.label_a_b as label_a_b,
c6c691a5 1048 civicrm_relationship_type.label_a_b as relation ';
6a488035
TO
1049 }
1050 }
1051
1052 $from = "
1053 FROM civicrm_relationship
1054INNER JOIN civicrm_relationship_type ON ( civicrm_relationship.relationship_type_id = civicrm_relationship_type.id )
1055INNER JOIN civicrm_contact ";
1056 if ($direction == 'a_b') {
1057 $from .= 'ON ( civicrm_contact.id = civicrm_relationship.contact_id_a ) ';
1058 }
1059 else {
1060 $from .= 'ON ( civicrm_contact.id = civicrm_relationship.contact_id_b ) ';
1061 }
1062 $from .= "
1063LEFT JOIN civicrm_address ON (civicrm_address.contact_id = civicrm_contact.id AND civicrm_address.is_primary = 1)
1064LEFT JOIN civicrm_phone ON (civicrm_phone.contact_id = civicrm_contact.id AND civicrm_phone.is_primary = 1)
1065LEFT JOIN civicrm_email ON (civicrm_email.contact_id = civicrm_contact.id AND civicrm_email.is_primary = 1)
1066LEFT JOIN civicrm_state_province ON (civicrm_address.state_province_id = civicrm_state_province.id)
1067LEFT JOIN civicrm_country ON (civicrm_address.country_id = civicrm_country.id)
1068";
1069 $where = 'WHERE ( 1 )';
1070 if ($contactId) {
1071 if ($direction == 'a_b') {
1072 $where .= ' AND civicrm_relationship.contact_id_b = ' . CRM_Utils_Type::escape($contactId, 'Positive');
1073 }
1074 else {
075fa74e 1075 $where .= ' AND civicrm_relationship.contact_id_a = ' . CRM_Utils_Type::escape($contactId, 'Positive') . '
1076 AND civicrm_relationship.contact_id_a != civicrm_relationship.contact_id_b ';
6a488035
TO
1077 }
1078 }
1079 if ($relationshipId) {
1080 $where .= ' AND civicrm_relationship.id = ' . CRM_Utils_Type::escape($relationshipId, 'Positive');
1081 }
1082
1083 $date = date('Y-m-d');
1084 if ($status == self::PAST) {
1085 //this case for showing past relationship
1086 $where .= ' AND civicrm_relationship.is_active = 1 ';
1087 $where .= " AND civicrm_relationship.end_date < '" . $date . "'";
1088 }
1089 elseif ($status == self::DISABLED) {
1090 // this case for showing disabled relationship
1091 $where .= ' AND civicrm_relationship.is_active = 0 ';
1092 }
1093 elseif ($status == self::CURRENT) {
1094 //this case for showing current relationship
1095 $where .= ' AND civicrm_relationship.is_active = 1 ';
1096 $where .= " AND (civicrm_relationship.end_date >= '" . $date . "' OR civicrm_relationship.end_date IS NULL) ";
1097 }
1098 elseif ($status == self::INACTIVE) {
1099 //this case for showing inactive relationships
1100 $where .= " AND (civicrm_relationship.end_date < '" . $date . "'";
1101 $where .= ' OR civicrm_relationship.is_active = 0 )';
1102 }
1103
1104 // CRM-6181
1105 $where .= ' AND civicrm_contact.is_deleted = 0';
22e263ad 1106 if (!empty($params['membership_type_id']) && empty($params['relationship_type_id'])) {
c9c41397 1107 $where .= self::membershipTypeToRelationshipTypes($params, $direction);
1108 }
22e263ad
TO
1109 if (!empty($params['relationship_type_id'])) {
1110 if (is_array($params['relationship_type_id'])) {
7f3647bc 1111 $where .= " AND " . CRM_Core_DAO::createSQLFilter('relationship_type_id', $params['relationship_type_id'], 'Integer');
75638074 1112 }
1113 else {
1114 $where .= ' AND relationship_type_id = ' . CRM_Utils_Type::escape($params['relationship_type_id'], 'Positive');
1115 }
1116 }
6a488035
TO
1117 if ($direction == 'a_b') {
1118 $where .= ' ) UNION ';
1119 }
1120 else {
1121 $where .= ' ) ';
1122 }
1123
1124 return array($select, $from, $where);
1125 }
1126
1127 /**
fe482240 1128 * Get a list of relationships.
6a488035 1129 *
77c5b619
TO
1130 * @param int $contactId
1131 * Contact id.
1132 * @param int $status
1133 * 1: Past 2: Disabled 3: Current.
1134 * @param int $numRelationship
1135 * No of relationships to display (limit).
1136 * @param int $count
1137 * Get the no of relationships.
77b97be7 1138 * @param int $relationshipId
76e7a76c
CW
1139 * @param array $links
1140 * the list of links to display
1141 * @param int $permissionMask
1142 * the permission mask to be applied for the actions
77b97be7 1143 * @param bool $permissionedContact
76e7a76c 1144 * to return only permissioned Contact
77b97be7
EM
1145 * @param array $params
1146 *
1147 * @return array|int
76e7a76c 1148 * relationship records
6a488035 1149 */
c301f76e 1150 public static function getRelationship(
51ccfbbe 1151 $contactId = NULL,
7f3647bc
TO
1152 $status = 0, $numRelationship = 0,
1153 $count = 0, $relationshipId = 0,
1154 $links = NULL, $permissionMask = NULL,
75638074 1155 $permissionedContact = FALSE,
1156 $params = array()
6a488035
TO
1157 ) {
1158 $values = array();
1159 if (!$contactId && !$relationshipId) {
1160 return $values;
1161 }
1162
1163 list($select1, $from1, $where1) = self::makeURLClause($contactId, $status, $numRelationship,
75638074 1164 $count, $relationshipId, 'a_b', $params
6a488035
TO
1165 );
1166 list($select2, $from2, $where2) = self::makeURLClause($contactId, $status, $numRelationship,
75638074 1167 $count, $relationshipId, 'b_a', $params
6a488035
TO
1168 );
1169
1170 $order = $limit = '';
1171 if (!$count) {
40458f6c 1172 if (empty($params['sort'])) {
1173 $order = ' ORDER BY civicrm_relationship_type_id, sort_name ';
1174 }
1175 else {
1176 $order = " ORDER BY {$params['sort']} ";
1177 }
1178
1179 $offset = 0;
630d1aaa 1180 if (!empty($params['offset']) && $params['offset'] > 0) {
40458f6c 1181 $offset = $params['offset'];
1182 }
6a488035
TO
1183
1184 if ($numRelationship) {
40458f6c 1185 $limit = " LIMIT {$offset}, $numRelationship";
6a488035
TO
1186 }
1187 }
1188
1189 // building the query string
6a488035
TO
1190 $queryString = $select1 . $from1 . $where1 . $select2 . $from2 . $where2 . $order . $limit;
1191
1192 $relationship = new CRM_Contact_DAO_Relationship();
1193
1194 $relationship->query($queryString);
1195 $row = array();
1196 if ($count) {
1197 $relationshipCount = 0;
1198 while ($relationship->fetch()) {
1199 $relationshipCount += $relationship->cnt1 + $relationship->cnt2;
1200 }
1201 return $relationshipCount;
1202 }
1203 else {
1204
1205 $mask = NULL;
1206 if ($status != self::INACTIVE) {
1207 if ($links) {
1208 $mask = array_sum(array_keys($links));
1209 if ($mask & CRM_Core_Action::DISABLE) {
1210 $mask -= CRM_Core_Action::DISABLE;
1211 }
1212 if ($mask & CRM_Core_Action::ENABLE) {
1213 $mask -= CRM_Core_Action::ENABLE;
1214 }
1215
1216 if ($status == self::CURRENT) {
1217 $mask |= CRM_Core_Action::DISABLE;
1218 }
1219 elseif ($status == self::DISABLED) {
1220 $mask |= CRM_Core_Action::ENABLE;
1221 }
1222 $mask = $mask & $permissionMask;
1223 }
1224 }
1225 while ($relationship->fetch()) {
1226 $rid = $relationship->civicrm_relationship_id;
1227 $cid = $relationship->civicrm_contact_id;
1228 if (($permissionedContact) &&
1229 (!CRM_Contact_BAO_Contact_Permission::relationship($cid, $contactId))
1230 ) {
1231 continue;
1232 }
1233 $values[$rid]['id'] = $rid;
1234 $values[$rid]['cid'] = $cid;
1235 $values[$rid]['contact_id_a'] = $relationship->contact_id_a;
1236 $values[$rid]['contact_id_b'] = $relationship->contact_id_b;
c6c691a5 1237 $values[$rid]['contact_type'] = $relationship->contact_type;
6a488035
TO
1238 $values[$rid]['relationship_type_id'] = $relationship->civicrm_relationship_type_id;
1239 $values[$rid]['relation'] = $relationship->relation;
1240 $values[$rid]['name'] = $relationship->sort_name;
1241 $values[$rid]['display_name'] = $relationship->display_name;
1242 $values[$rid]['job_title'] = $relationship->job_title;
1243 $values[$rid]['email'] = $relationship->email;
1244 $values[$rid]['phone'] = $relationship->phone;
1245 $values[$rid]['employer_id'] = $relationship->employer_id;
1246 $values[$rid]['organization_name'] = $relationship->organization_name;
1247 $values[$rid]['country'] = $relationship->country;
1248 $values[$rid]['city'] = $relationship->city;
1249 $values[$rid]['state'] = $relationship->state;
1250 $values[$rid]['start_date'] = $relationship->start_date;
1251 $values[$rid]['end_date'] = $relationship->end_date;
1252 $values[$rid]['description'] = $relationship->description;
1253 $values[$rid]['is_active'] = $relationship->is_active;
1254 $values[$rid]['is_permission_a_b'] = $relationship->is_permission_a_b;
1255 $values[$rid]['is_permission_b_a'] = $relationship->is_permission_b_a;
1256 $values[$rid]['case_id'] = $relationship->case_id;
1257
1258 if ($status) {
1259 $values[$rid]['status'] = $status;
1260 }
1261
1262 $values[$rid]['civicrm_relationship_type_id'] = $relationship->civicrm_relationship_type_id;
1263
1264 if ($relationship->contact_id_a == $contactId) {
1265 $values[$rid]['rtype'] = 'a_b';
1266 }
1267 else {
1268 $values[$rid]['rtype'] = 'b_a';
1269 }
1270
1271 if ($links) {
1272 $replace = array(
1273 'id' => $rid,
1274 'rtype' => $values[$rid]['rtype'],
1275 'cid' => $contactId,
1276 'cbid' => $values[$rid]['cid'],
1277 'caseid' => $values[$rid]['case_id'],
1278 'clientid' => $contactId,
1279 );
1280
1281 if ($status == self::INACTIVE) {
1282 // setting links for inactive relationships
1283 $mask = array_sum(array_keys($links));
1284 if (!$values[$rid]['is_active']) {
1285 $mask -= CRM_Core_Action::DISABLE;
1286 }
1287 else {
1288 $mask -= CRM_Core_Action::ENABLE;
1289 $mask -= CRM_Core_Action::DISABLE;
1290 }
1291 }
1292
1293 // Give access to manage case link by copying to MAX_ACTION index temporarily, depending on case permission of user.
1294 if ($values[$rid]['case_id']) {
1295 // Borrowed logic from CRM_Case_Page_Tab
1296 $hasCaseAccess = FALSE;
1297 if (CRM_Core_Permission::check('access all cases and activities')) {
1298 $hasCaseAccess = TRUE;
1299 }
1300 else {
1301 $userCases = CRM_Case_BAO_Case::getCases(FALSE);
1302 if (array_key_exists($values[$rid]['case_id'], $userCases)) {
1303 $hasCaseAccess = TRUE;
1304 }
1305 }
1306
1307 if ($hasCaseAccess) {
1308 // give access by copying to MAX_ACTION temporarily, otherwise leave at NONE which won't display
1309 $links[CRM_Core_Action::MAX_ACTION] = $links[CRM_Core_Action::NONE];
1310 $links[CRM_Core_Action::MAX_ACTION]['name'] = ts('Manage Case #%1', array(1 => $values[$rid]['case_id']));
f89f2102 1311 $links[CRM_Core_Action::MAX_ACTION]['class'] = 'no-popup';
6a488035
TO
1312
1313 // Also make sure we have the right client cid since can get here from multiple relationship tabs.
1314 if ($values[$rid]['rtype'] == 'b_a') {
1315 $replace['clientid'] = $values[$rid]['cid'];
1316 }
1317 }
1318 }
1319
87dab4a4 1320 $values[$rid]['action'] = CRM_Core_Action::formLink(
1cfa04c4
EM
1321 $links,
1322 $mask,
87dab4a4
AH
1323 $replace,
1324 ts('more'),
1325 FALSE,
1326 'relationship.selector.row',
1327 'Relationship',
1328 $rid);
6a488035
TO
1329 unset($links[CRM_Core_Action::MAX_ACTION]);
1330 }
1331 }
1332
1333 $relationship->free();
1334 return $values;
1335 }
1336 }
1337
1338 /**
100fef9d 1339 * Get get list of relationship type based on the target contact type.
6a488035 1340 *
77c5b619
TO
1341 * @param string $targetContactType
1342 * It's valid contact tpye(may be Individual , Organization , Household).
6a488035 1343 *
a6c01b45
CW
1344 * @return array
1345 * array reference of all relationship types with context to current contact type .
6a488035 1346 */
ee17a760 1347 static public function getRelationType($targetContactType) {
6a488035
TO
1348 $relationshipType = array();
1349 $allRelationshipType = CRM_Core_PseudoConstant::relationshipType();
1350
1351 foreach ($allRelationshipType as $key => $type) {
1352 if ($type['contact_type_b'] == $targetContactType) {
1353 $relationshipType[$key . '_a_b'] = $type['label_a_b'];
1354 }
1355 }
1356
1357 return $relationshipType;
1358 }
1359
1360 /**
100fef9d 1361 * Create / update / delete membership for related contacts.
6a488035
TO
1362 *
1363 * This function will create/update/delete membership for related
1364 * contact based on 1) contact have active membership 2) that
1365 * membership is is extedned by the same relationship type to that
1366 * of the existing relationship.
1367 *
5a4f6742
CW
1368 * @param int $contactId
1369 * contact id.
1370 * @param array $params
1371 * array of values submitted by POST.
1372 * @param array $ids
1373 * array of ids.
bfa4da96 1374 * @param \const|int $action which action called this function
6a488035 1375 *
dd244018 1376 * @param bool $active
6a488035 1377 *
bfa4da96 1378 * @throws \CRM_Core_Exception
6a488035 1379 */
00be9182 1380 public static function relatedMemberships($contactId, &$params, $ids, $action = CRM_Core_Action::ADD, $active = TRUE) {
6a488035 1381 // Check the end date and set the status of the relationship
decc60a0 1382 // accordingly.
6a488035 1383 $status = self::CURRENT;
04ad3598 1384 $targetContact = $targetContact = CRM_Utils_Array::value('contact_check', $params, array());
6a488035
TO
1385
1386 if (!empty($params['end_date'])) {
1387 $endDate = CRM_Utils_Date::setDateDefaults(CRM_Utils_Date::format($params['end_date']), NULL, 'Ymd');
1388 $today = date('Ymd');
1389
1390 if ($today > $endDate) {
1391 $status = self::PAST;
1392 }
1393 }
1394
1395 if (($action & CRM_Core_Action::ADD) &&
1396 ($status & self::PAST)
1397 ) {
1398 // if relationship is PAST and action is ADD, no qustion
1399 // of creating RELATED membership and return back to
1400 // calling method
1401 return;
1402 }
1403
1404 $rel = explode('_', $params['relationship_type_id']);
1405
7f3647bc 1406 $relTypeId = $rel[0];
decc60a0
EM
1407 if (!empty($rel[1])) {
1408 $relDirection = "_{$rel[1]}_{$rel[2]}";
1409 }
1410 else {
1411 // this call is coming from somewhere where the direction was resolved early on (e.g an api call)
1412 // so we can assume _a_b
1413 $relDirection = "_a_b";
04ad3598 1414 $targetContact = array($params['contact_id_b'] => 1);
decc60a0 1415 }
04ad3598 1416
6a488035
TO
1417 if (($action & CRM_Core_Action::ADD) ||
1418 ($action & CRM_Core_Action::DELETE)
1419 ) {
1420 $contact = $contactId;
6a488035
TO
1421 }
1422 elseif ($action & CRM_Core_Action::UPDATE) {
1423 $contact = $ids['contact'];
1424 $targetContact = array($ids['contactTarget'] => 1);
1425 }
1426
1427 // Build the 'values' array for
1428 // 1. ContactA
1429 // 2. ContactB
1430 // This will allow us to check if either of the contacts in
1431 // relationship have active memberships.
1432
1433 $values = array();
1434
1435 // 1. ContactA
1436 $values[$contact] = array(
1437 'relatedContacts' => $targetContact,
1438 'relationshipTypeId' => $relTypeId,
1439 'relationshipTypeDirection' => $relDirection,
1440 );
1441 // 2. ContactB
1442 if (!empty($targetContact)) {
1443 foreach ($targetContact as $cid => $donCare) {
1444 $values[$cid] = array(
1445 'relatedContacts' => array($contact => 1),
1446 'relationshipTypeId' => $relTypeId,
1447 );
1448
1449 $relTypeParams = array('id' => $relTypeId);
1450 $relTypeValues = array();
1451 CRM_Contact_BAO_RelationshipType::retrieve($relTypeParams, $relTypeValues);
1452
1453 if (CRM_Utils_Array::value('name_a_b', $relTypeValues) == CRM_Utils_Array::value('name_b_a', $relTypeValues)) {
1454 $values[$cid]['relationshipTypeDirection'] = '_a_b';
1455 }
1456 else {
1457 $values[$cid]['relationshipTypeDirection'] = ($relDirection == '_a_b') ? '_b_a' : '_a_b';
1458 }
1459 }
1460 }
1461
bd655ee6
MV
1462 // CRM-15829 UPDATES
1463 // If we're looking for active memberships we must consider pending (id: 5) ones too.
1464 // Hence we can't just call CRM_Member_BAO_Membership::getValues below with the active flag, is it would completely miss pending relatioships.
3421dcee
MV
1465 // As suggested by @davecivicrm, the pending status id is fetched using the CRM_Member_PseudoConstant::membershipStatus() class and method, since these ids differ from system to system.
1466 $pendingStatusId = array_search('Pending', CRM_Member_PseudoConstant::membershipStatus());
bd655ee6
MV
1467
1468 $query = 'SELECT * FROM `civicrm_membership_status`';
1469 if ($active) {
3421dcee 1470 $query .= 'WHERE `is_current_member` = 1 OR `id` = %1 ';
bd655ee6
MV
1471 }
1472
3421dcee
MV
1473 $params[1] = array($pendingStatusId, 'String');
1474 $dao = CRM_Core_DAO::executeQuery($query, $params);
1475
bd655ee6
MV
1476 while ($dao->fetch()) {
1477 $membershipStatusRecordIds[$dao->id] = $dao->id;
1478 }
1479
6a488035
TO
1480 // Now get the active memberships for all the contacts.
1481 // If contact have any valid membership(s), then add it to
1482 // 'values' array.
1483 foreach ($values as $cid => $subValues) {
1484 $memParams = array('contact_id' => $cid);
1485 $memberships = array();
1486
bd655ee6
MV
1487 // CRM-15829 UPDATES
1488 // Since we want PENDING memberships as well, the $active flag needs to be set to false so that this will return all memberships and we can then filter the memberships based on the status IDs recieved above.
1489 CRM_Member_BAO_Membership::getValues($memParams, $memberships, FALSE, TRUE);
1490
1491 // CRM-15829 UPDATES
1492 // filter out the memberships returned by CRM_Member_BAO_Membership::getValues based on the status IDs fetched on line ~1462
1493 foreach ($memberships as $key => $membership) {
1494
1495 if (!isset($memberships[$key]['status_id'])) {
1496 continue;
1497 }
1498
1499 $membershipStatusId = $memberships[$key]['status_id'];
1500 if (!isset($membershipStatusRecordIds[$membershipStatusId])) {
1501 unset($memberships[$key]);
1502 }
1503 }
6a488035
TO
1504
1505 if (empty($memberships)) {
1506 continue;
1507 }
1508
f1e48e7f 1509 //get ownerMembershipIds for related Membership
1510 //this is to handle memberships being deleted and recreated
1511 if (!empty($memberships['owner_membership_ids'])) {
91b00626 1512 $ownerMemIds[$cid] = $memberships['owner_membership_ids'];
f1e48e7f 1513 unset($memberships['owner_membership_ids']);
1514 }
1515
6a488035
TO
1516 $values[$cid]['memberships'] = $memberships;
1517 }
1518 $deceasedStatusId = array_search('Deceased', CRM_Member_PseudoConstant::membershipStatus());
1519
1520 // done with 'values' array.
1521 // Finally add / edit / delete memberships for the related contacts
ee17a760 1522
6a488035
TO
1523 foreach ($values as $cid => $details) {
1524 if (!array_key_exists('memberships', $details)) {
1525 continue;
1526 }
1527
25f1372e 1528 $relatedContacts = array_keys(CRM_Utils_Array::value('relatedContacts', $details, array()));
ee17a760 1529 $mainRelatedContactId = reset($relatedContacts);
6a488035
TO
1530
1531 foreach ($details['memberships'] as $membershipId => $membershipValues) {
1532 $relTypeIds = array();
1533 if ($action & CRM_Core_Action::DELETE) {
1534 // Delete memberships of the related contacts only if relationship type exists for membership type
1535 $query = "
1536SELECT relationship_type_id, relationship_direction
1537 FROM civicrm_membership_type
1538 WHERE id = {$membershipValues['membership_type_id']}";
1539 $dao = CRM_Core_DAO::executeQuery($query);
1540 $relTypeDirs = array();
1541 while ($dao->fetch()) {
1542 $relTypeId = $dao->relationship_type_id;
1543 $relDirection = $dao->relationship_direction;
1544 }
1545 $relTypeIds = explode(CRM_Core_DAO::VALUE_SEPARATOR, $relTypeId);
4d88abcc 1546 if (in_array($values[$cid]['relationshipTypeId'], $relTypeIds
1547 //CRM-16300 check if owner membership exist for related membership
1548 ) && !empty($membershipValues['owner_membership_id']) && !empty($values[$mainRelatedContactId]['memberships'][$membershipValues['owner_membership_id']])) {
690c56c3 1549 CRM_Member_BAO_Membership::deleteRelatedMemberships($membershipValues['owner_membership_id'], $membershipValues['membership_contact_id']);
6a488035
TO
1550 }
1551 continue;
1552 }
1553 if (($action & CRM_Core_Action::UPDATE) &&
1554 ($status & self::PAST) &&
1555 ($membershipValues['owner_membership_id'])
1556 ) {
1557 // If relationship is PAST and action is UPDATE
1558 // then delete the RELATED membership
1559 CRM_Member_BAO_Membership::deleteRelatedMemberships($membershipValues['owner_membership_id'],
1560 $membershipValues['membership_contact_id']
1561 );
1562 continue;
1563 }
1564
1565 // add / edit the memberships for related
1566 // contacts.
1567
1568 // Get the Membership Type Details.
1569 $membershipType = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($membershipValues['membership_type_id']);
1570 // Check if contact's relationship type exists in membership type
1571 $relTypeDirs = array();
a7488080 1572 if (!empty($membershipType['relationship_type_id'])) {
6a488035
TO
1573 $relTypeIds = explode(CRM_Core_DAO::VALUE_SEPARATOR, $membershipType['relationship_type_id']);
1574 }
a7488080 1575 if (!empty($membershipType['relationship_direction'])) {
6a488035
TO
1576 $relDirections = explode(CRM_Core_DAO::VALUE_SEPARATOR, $membershipType['relationship_direction']);
1577 }
1578 foreach ($relTypeIds as $key => $value) {
1579 $relTypeDirs[] = $value . '_' . $relDirections[$key];
1580 }
1581 $relTypeDir = $details['relationshipTypeId'] . $details['relationshipTypeDirection'];
1582 if (in_array($relTypeDir, $relTypeDirs)) {
1583 // Check if relationship being created/updated is
1584 // similar to that of membership type's
1585 // relationship.
1586
1587 $membershipValues['owner_membership_id'] = $membershipId;
1588 unset($membershipValues['id']);
1589 unset($membershipValues['membership_contact_id']);
1590 unset($membershipValues['contact_id']);
1591 unset($membershipValues['membership_id']);
1592 foreach ($details['relatedContacts'] as $relatedContactId => $donCare) {
1593 $membershipValues['contact_id'] = $relatedContactId;
1594 if ($deceasedStatusId &&
1595 CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $relatedContactId, 'is_deceased')
1596 ) {
1597 $membershipValues['status_id'] = $deceasedStatusId;
1598 $membershipValues['skipStatusCal'] = TRUE;
1599 }
1600 foreach (array(
7f3647bc
TO
1601 'join_date',
1602 'start_date',
21dfd5f5 1603 'end_date',
7f3647bc 1604 ) as $dateField) {
a7488080 1605 if (!empty($membershipValues[$dateField])) {
6a488035
TO
1606 $membershipValues[$dateField] = CRM_Utils_Date::processDate($membershipValues[$dateField]);
1607 }
1608 }
1609
1610 if ($action & CRM_Core_Action::UPDATE) {
eec8d770 1611 //if updated relationship is already related to contact don't delete existing inherited membership
4d88abcc 1612 if (in_array($relTypeId, $relTypeIds
f1e48e7f 1613 ) && !empty($values[$relatedContactId]['memberships']) && !empty($ownerMemIds
1614 ) && in_array($membershipValues['owner_membership_id'], $ownerMemIds[$relatedContactId])) {
eec8d770 1615 continue;
1616 }
f1e48e7f 1617
6a488035
TO
1618 //delete the membership record for related
1619 //contact before creating new membership record.
1620 CRM_Member_BAO_Membership::deleteRelatedMemberships($membershipId, $relatedContactId);
1621 }
1622
1623 // check whether we have some related memberships still available
1624 $query = "
1625SELECT count(*)
1626 FROM civicrm_membership
1627 LEFT JOIN civicrm_membership_status ON (civicrm_membership_status.id = civicrm_membership.status_id)
1628 WHERE membership_type_id = {$membershipValues['membership_type_id']} AND owner_membership_id = {$membershipValues['owner_membership_id']}
1629 AND is_current_member = 1";
1630 $result = CRM_Core_DAO::singleValueQuery($query);
1631 if ($result < CRM_Utils_Array::value('max_related', $membershipValues, PHP_INT_MAX)) {
43291913 1632 CRM_Member_BAO_Membership::create($membershipValues, CRM_Core_DAO::$_nullArray);
6a488035
TO
1633 }
1634 }
1635 }
1636 elseif ($action & CRM_Core_Action::UPDATE) {
1637 // if action is update and updated relationship do
1638 // not match with the existing
1639 // membership=>relationship then we need to
1640 // delete the membership record created for
1641 // previous relationship.
690c56c3 1642 // CRM-16087 we need to pass ownerMembershipId to deleteRelatedMemberships function
1643 if (empty($params['relationship_ids']) && !empty($params['id'])) {
1644 $relIds = array($params['id']);
1645 }
1646 else {
1647 $relIds = CRM_Utils_Array::value('relationship_ids', $params);
1648 }
1649 if (self::isDeleteRelatedMembership($relTypeIds, $contactId, $mainRelatedContactId, $relTypeId,
4d88abcc 1650 $relIds) && !empty($membershipValues['owner_membership_id']
1651 ) && !empty($values[$mainRelatedContactId]['memberships'][$membershipValues['owner_membership_id']])) {
690c56c3 1652 CRM_Member_BAO_Membership::deleteRelatedMemberships($membershipValues['owner_membership_id'], $membershipValues['membership_contact_id']);
6a488035
TO
1653 }
1654 }
1655 }
1656 }
1657 }
1658
1659 /**
98a06ae0 1660 * Helper function to check whether to delete the membership or not.
6616f7c1
EM
1661 *
1662 * Function takes a list of related membership types and if it is not also passed a
1663 * relationship ID of that types evaluates whether the membership should be deleted.
1664 *
1665 * @param array $membershipTypeRelationshipTypeIDs
1666 * Relation type IDs related to the given membership type.
1667 * @param int $contactId
1668 * @param int $mainRelatedContactId
1669 * @param int $relTypeId
1670 * @param array $relIds
1671 *
1672 * @return bool
6a488035 1673 */
6616f7c1
EM
1674 public static function isDeleteRelatedMembership($membershipTypeRelationshipTypeIDs, $contactId, $mainRelatedContactId, $relTypeId, $relIds) {
1675 if (empty($membershipTypeRelationshipTypeIDs) || in_array($relTypeId, $membershipTypeRelationshipTypeIDs)) {
eec8d770 1676 return FALSE;
6a488035
TO
1677 }
1678
1679 if (empty($relIds)) {
1680 return FALSE;
1681 }
1682
7f3647bc
TO
1683 $relParamas = array(
1684 1 => array($contactId, 'Integer'),
6a488035
TO
1685 2 => array($mainRelatedContactId, 'Integer'),
1686 );
1687
1688 if ($contactId == $mainRelatedContactId) {
6616f7c1
EM
1689 $recordsFound = (int) CRM_Core_DAO::singleValueQuery("SELECT COUNT(*) FROM civicrm_relationship WHERE relationship_type_id IN ( " . implode(',', $membershipTypeRelationshipTypeIDs) . " ) AND
1690contact_id_a IN ( %1 ) OR contact_id_b IN ( %1 ) AND id IN (" . implode(',', $relIds) . ")", $relParamas);
6a488035
TO
1691 if ($recordsFound) {
1692 return FALSE;
1693 }
1694 return TRUE;
1695 }
1696
6616f7c1 1697 $recordsFound = (int) CRM_Core_DAO::singleValueQuery("SELECT COUNT(*) FROM civicrm_relationship WHERE relationship_type_id IN ( " . implode(',', $membershipTypeRelationshipTypeIDs) . " ) AND contact_id_a IN ( %1, %2 ) AND contact_id_b IN ( %1, %2 ) AND id NOT IN (" . implode(',', $relIds) . ")", $relParamas);
6a488035
TO
1698
1699 if ($recordsFound) {
1700 return FALSE;
1701 }
1702
1703 return TRUE;
1704 }
1705
1706 /**
fe482240 1707 * Get Current Employer for Contact.
6a488035 1708 *
77c5b619
TO
1709 * @param $contactIds
1710 * Contact Ids.
6a488035 1711 *
a6c01b45
CW
1712 * @return array
1713 * array of the current employer
6a488035 1714 */
00be9182 1715 public static function getCurrentEmployer($contactIds) {
6a488035
TO
1716 $contacts = implode(',', $contactIds);
1717
1718 $query = "
1719SELECT organization_name, id, employer_id
1720FROM civicrm_contact
1721WHERE id IN ( {$contacts} )
1722";
1723
1724 $dao = CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
1725 $currentEmployer = array();
1726 while ($dao->fetch()) {
1727 $currentEmployer[$dao->id]['org_id'] = $dao->employer_id;
1728 $currentEmployer[$dao->id]['org_name'] = $dao->organization_name;
1729 }
1730
1731 return $currentEmployer;
1732 }
1733
1734 /**
100fef9d 1735 * Return list of permissioned employer for a given contact.
6a488035 1736 *
5a4f6742
CW
1737 * @param int $contactID
1738 * contact id whose employers.
16b10e64 1739 * are to be found.
5a4f6742
CW
1740 * @param string $name
1741 * employers sort name.
6a488035 1742 *
a6c01b45
CW
1743 * @return array
1744 * array of employers.
6a488035 1745 */
00be9182 1746 public static function getPermissionedEmployer($contactID, $name = NULL) {
6a488035
TO
1747 //get the relationship id
1748 $relTypeId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType',
1749 'Employee of', 'id', 'name_a_b'
1750 );
1751
51e89def
DS
1752 return self::getPermissionedContacts($contactID, $relTypeId, $name);
1753 }
1754
1755
7f3647bc 1756 /**
fe482240 1757 * Function to return list of permissioned contacts for a given contact and relationship type.
7f3647bc 1758 *
5a4f6742
CW
1759 * @param int $contactID
1760 * contact id whose permissioned contacts are to be found.
1761 * @param string $relTypeId
1762 * one or more relationship type id's.
1763 * @param string $name
7f3647bc 1764 *
a6c01b45 1765 * @return array
16b10e64 1766 * Array of contacts
7f3647bc 1767 */
00be9182 1768 public static function getPermissionedContacts($contactID, $relTypeId, $name = NULL) {
51e89def
DS
1769 $contacts = array();
1770
6a488035
TO
1771 if ($relTypeId) {
1772 $query = "
1773SELECT cc.id as id, cc.sort_name as name
1774FROM civicrm_relationship cr, civicrm_contact cc
49f8272d 1775WHERE
51e89def 1776cr.contact_id_a = %1 AND
f9f0eff9 1777cr.relationship_type_id IN (%2) AND
51e89def 1778cr.is_permission_a_b = 1 AND
6a488035
TO
1779IF(cr.end_date IS NULL, 1, (DATEDIFF( CURDATE( ), cr.end_date ) <= 0)) AND
1780cr.is_active = 1 AND
7ecf6275 1781cc.id = cr.contact_id_b AND
4755bcde 1782cc.is_deleted = 0";
51e89def
DS
1783
1784 if (!empty($name)) {
7f3647bc 1785 $name = CRM_Utils_Type::escape($name, 'String');
51e89def 1786 $query .= "
49f8272d 1787AND cc.sort_name LIKE '%$name%'";
51e89def 1788 }
6a488035 1789
f9f0eff9 1790 $args = array(1 => array($contactID, 'Integer'), 2 => array($relTypeId, 'String'));
7f3647bc 1791 $dao = CRM_Core_DAO::executeQuery($query, $args);
6a488035
TO
1792
1793 while ($dao->fetch()) {
51e89def 1794 $contacts[$dao->id] = array(
6a488035
TO
1795 'name' => $dao->name,
1796 'value' => $dao->id,
1797 );
1798 }
1799 }
51e89def 1800 return $contacts;
6a488035
TO
1801 }
1802
6a488035 1803 /**
fe482240 1804 * Merge relationships from otherContact to mainContact.
bfa4da96 1805 *
6a488035
TO
1806 * Called during contact merge operation
1807 *
77c5b619
TO
1808 * @param int $mainId
1809 * Contact id of main contact record.
1810 * @param int $otherId
1811 * Contact id of record which is going to merge.
1812 * @param array $sqls
1813 * (reference) array of sql statements to append to.
6a488035
TO
1814 *
1815 * @see CRM_Dedupe_Merger::cpTables()
6a488035 1816 */
00be9182 1817 public static function mergeRelationships($mainId, $otherId, &$sqls) {
6a488035
TO
1818 // Delete circular relationships
1819 $sqls[] = "DELETE FROM civicrm_relationship
1820 WHERE (contact_id_a = $mainId AND contact_id_b = $otherId)
1821 OR (contact_id_b = $mainId AND contact_id_a = $otherId)";
1822
1823 // Delete relationship from other contact if main contact already has that relationship
1824 $sqls[] = "DELETE r2
1825 FROM civicrm_relationship r1, civicrm_relationship r2
1826 WHERE r1.relationship_type_id = r2.relationship_type_id
1827 AND r1.id <> r2.id
1828 AND (
1829 r1.contact_id_a = $mainId AND r2.contact_id_a = $otherId AND r1.contact_id_b = r2.contact_id_b
1830 OR r1.contact_id_b = $mainId AND r2.contact_id_b = $otherId AND r1.contact_id_a = r2.contact_id_a
1831 OR (
1832 (r1.contact_id_a = $mainId AND r2.contact_id_b = $otherId AND r1.contact_id_b = r2.contact_id_a
1833 OR r1.contact_id_b = $mainId AND r2.contact_id_a = $otherId AND r1.contact_id_a = r2.contact_id_b)
1834 AND r1.relationship_type_id IN (SELECT id FROM civicrm_relationship_type WHERE name_b_a = name_a_b)
1835 )
1836 )";
1837
1838 // Move relationships
1839 $sqls[] = "UPDATE IGNORE civicrm_relationship SET contact_id_a = $mainId WHERE contact_id_a = $otherId";
1840 $sqls[] = "UPDATE IGNORE civicrm_relationship SET contact_id_b = $mainId WHERE contact_id_b = $otherId";
1841
1842 // Move current employer id (name will get updated later)
1843 $sqls[] = "UPDATE civicrm_contact SET employer_id = $mainId WHERE employer_id = $otherId";
1844 }
1845
1846 /**
1847 * Set 'is_valid' field to false for all relationships whose end date is in the past, ie. are expired.
1848 *
72b3a70c
CW
1849 * @return bool
1850 * True on success, false if error is encountered.
6a488035 1851 */
00be9182 1852 public static function disableExpiredRelationships() {
6a488035
TO
1853 $query = "SELECT id FROM civicrm_relationship WHERE is_active = 1 AND end_date < CURDATE()";
1854
1855 $dao = CRM_Core_DAO::executeQuery($query);
1856 while ($dao->fetch()) {
1857 $result = CRM_Contact_BAO_Relationship::setIsActive($dao->id, FALSE);
1858 // Result will be NULL if error occurred. We abort early if error detected.
1859 if ($result == NULL) {
1860 return FALSE;
1861 }
1862 }
1863 return TRUE;
1864 }
c9c41397 1865
1866 /**
fe482240 1867 * Function filters the query by possible relationships for the membership type.
98a06ae0 1868 *
c9c41397 1869 * It is intended to be called when constructing queries for the api (reciprocal & non-reciprocal)
1870 * and to add clauses to limit the return to those relationships which COULD inherit a membership type
1871 * (as opposed to those who inherit a particular membership
1872 *
77c5b619
TO
1873 * @param array $params
1874 * Api input array.
77b97be7
EM
1875 * @param null $direction
1876 *
c301f76e 1877 * @return array|void
c9c41397 1878 */
00be9182 1879 public static function membershipTypeToRelationshipTypes(&$params, $direction = NULL) {
7f3647bc 1880 $membershipType = civicrm_api3('membership_type', 'getsingle', array(
353ffa53
TO
1881 'id' => $params['membership_type_id'],
1882 'return' => 'relationship_type_id, relationship_direction',
1883 ));
c9c41397 1884 $relationshipTypes = $membershipType['relationship_type_id'];
22e263ad 1885 if (empty($relationshipTypes)) {
c301f76e 1886 return NULL;
f201289d 1887 }
c9c41397 1888 // if we don't have any contact data we can only filter on type
22e263ad 1889 if (empty($params['contact_id']) && empty($params['contact_id_a']) && empty($params['contact_id_a'])) {
c9c41397 1890 $params['relationship_type_id'] = array('IN' => $relationshipTypes);
c301f76e 1891 return NULL;
c9c41397 1892 }
1893 else {
1a9b2ee8 1894 $relationshipDirections = (array) $membershipType['relationship_direction'];
c9c41397 1895 // if we have contact_id_a OR contact_id_b we can make a call here
1896 // if we have contact??
1897 foreach ($relationshipDirections as $index => $mtdirection) {
7f3647bc 1898 if (isset($params['contact_id_a']) && $mtdirection == 'a_b' || $direction == 'a_b') {
c9c41397 1899 $types[] = $relationshipTypes[$index];
1900 }
7f3647bc 1901 if (isset($params['contact_id_b']) && $mtdirection == 'b_a' || $direction == 'b_a') {
c9c41397 1902 $types[] = $relationshipTypes[$index];
1903 }
1904 }
22e263ad 1905 if (!empty($types)) {
c9c41397 1906 $params['relationship_type_id'] = array('IN' => $types);
1907 }
7f3647bc 1908 elseif (!empty($clauses)) {
c9c41397 1909 return explode(' OR ', $clauses);
1910 }
92e4c2a5 1911 else {
c9c41397 1912 // effectively setting it to return no results
1913 $params['relationship_type_id'] = 0;
1914 }
1915 }
1916 }
40458f6c 1917
1918
1919 /**
98a06ae0 1920 * Wrapper for contact relationship selector.
40458f6c 1921 *
77c5b619
TO
1922 * @param array $params
1923 * Associated array for params record id.
40458f6c 1924 *
a6c01b45
CW
1925 * @return array
1926 * associated array of contact relationships
40458f6c 1927 */
1928 public static function getContactRelationshipSelector(&$params) {
1929 // format the params
7f3647bc
TO
1930 $params['offset'] = ($params['page'] - 1) * $params['rp'];
1931 $params['sort'] = CRM_Utils_Array::value('sortBy', $params);
40458f6c 1932
1933 if ($params['context'] == 'past') {
1934 $relationshipStatus = CRM_Contact_BAO_Relationship::INACTIVE;
1935 }
62e6afce 1936 elseif ($params['context'] == 'all') {
6c292184
TM
1937 $relationshipStatus = CRM_Contact_BAO_Relationship::ALL;
1938 }
40458f6c 1939 else {
1940 $relationshipStatus = CRM_Contact_BAO_Relationship::CURRENT;
1941 }
1942
1943 // check logged in user for permission
1944 $page = new CRM_Core_Page();
1945 CRM_Contact_Page_View::checkUserPermission($page, $params['contact_id']);
1946 $permissions = array($page->_permission);
1947 if ($page->_permission == CRM_Core_Permission::EDIT) {
1948 $permissions[] = CRM_Core_Permission::DELETE;
1949 }
1950 $mask = CRM_Core_Action::mask($permissions);
1951
d0592c3d 1952 if ($params['context'] != 'user') {
1953 $links = CRM_Contact_Page_View_Relationship::links();
1954 $permissionedContacts = FALSE;
1955 }
1956 else {
1957 $links = CRM_Contact_Page_View_UserDashBoard::links();
1958 $permissionedContacts = TRUE;
1959 $mask = NULL;
1960 }
40458f6c 1961 // get contact relationships
1962 $relationships = CRM_Contact_BAO_Relationship::getRelationship($params['contact_id'],
1963 $relationshipStatus,
1964 $params['rp'], 0, 0,
d0592c3d 1965 $links, $mask,
1966 $permissionedContacts,
40458f6c 1967 $params
1968 );
1969
1970 $contactRelationships = array();
d0592c3d 1971 $params['total'] = 0;
40458f6c 1972 if (!empty($relationships)) {
d0592c3d 1973 // get the total relationships
1974 if ($params['context'] != 'user') {
1975 $params['total'] = CRM_Contact_BAO_Relationship::getRelationship($params['contact_id'],
7f3647bc 1976 $relationshipStatus, 0, 1, 0, NULL, NULL, $permissionedContacts);
d0592c3d 1977 }
1978 else {
1979 // FIX ME: we cannot directly determine total permissioned relationship, hence re-fire query
1980 $permissionedRelationships = CRM_Contact_BAO_Relationship::getRelationship($params['contact_id'],
1981 $relationshipStatus,
1982 0, 0, 0,
1983 NULL, NULL, TRUE
1984 );
1985 $params['total'] = count($permissionedRelationships);
1986 }
40458f6c 1987
1988 // format params
1989 foreach ($relationships as $relationshipId => $values) {
67e4fb51 1990 //Add image icon for related contacts: CRM-14919
c6c691a5 1991 $icon = CRM_Contact_BAO_Contact_Utils::getImage($values['contact_type'],
67e4fb51
N
1992 FALSE,
1993 $values['cid']
1994 );
92fcb95f 1995 $contactRelationships[$relationshipId]['name'] = $icon . ' ' . CRM_Utils_System::href(
7f3647bc
TO
1996 $values['name'],
1997 'civicrm/contact/view',
1998 "reset=1&cid={$values['cid']}");
40458f6c 1999
2000 $contactRelationships[$relationshipId]['relation'] = CRM_Utils_System::href(
2001 $values['relation'],
2002 'civicrm/contact/view/rel',
f1cb718d 2003 "action=view&reset=1&cid={$values['cid']}&id={$values['id']}&rtype={$values['rtype']}");
40458f6c 2004
d0592c3d 2005 if ($params['context'] == 'current') {
40458f6c 2006 if (($params['contact_id'] == $values['contact_id_a'] AND $values['is_permission_a_b'] == 1) OR
2007 ($params['contact_id'] == $values['contact_id_b'] AND $values['is_permission_b_a'] == 1)
2008 ) {
2009 $contactRelationships[$relationshipId]['name'] .= '<span id="permission-a-b" class="crm-marker permission-relationship"> *</span>';
2010 }
2011
2012 if (($values['cid'] == $values['contact_id_a'] AND $values['is_permission_a_b'] == 1) OR
2013 ($values['cid'] == $values['contact_id_b'] AND $values['is_permission_b_a'] == 1)
2014 ) {
2015 $contactRelationships[$relationshipId]['relation'] .= '<span id="permission-b-a" class="crm-marker permission-relationship"> *</span>';
2016 }
2017 }
2018
2019 if (!empty($values['description'])) {
2020 $contactRelationships[$relationshipId]['relation'] .= "<p class='description'>{$values['description']}</p>";
2021 }
2022
2023 $contactRelationships[$relationshipId]['start_date'] = CRM_Utils_Date::customFormat($values['start_date']);
2024 $contactRelationships[$relationshipId]['end_date'] = CRM_Utils_Date::customFormat($values['end_date']);
2025 $contactRelationships[$relationshipId]['city'] = $values['city'];
2026 $contactRelationships[$relationshipId]['state'] = $values['state'];
2027 $contactRelationships[$relationshipId]['email'] = $values['email'];
2028 $contactRelationships[$relationshipId]['phone'] = $values['phone'];
2029 $contactRelationships[$relationshipId]['links'] = $values['action'];
e1cad725 2030 $contactRelationships[$relationshipId]['id'] = $values['id'];
f1321272 2031 $contactRelationships[$relationshipId]['is_active'] = $values['is_active'];
40458f6c 2032 }
2033 }
2034 return $contactRelationships;
2035 }
2036
6a488035 2037}