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