Merge pull request #6548 from eileenmcnaughton/CRM-16512
[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
ef6463ad
SB
192 //CRM-16978:check duplicate relationship as per case id.
193 if ($caseId = CRM_Utils_Array::value('case_id', $params)) {
194 $contactFields['case_id'] = $caseId;
195 }
6a488035 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) ||
564 in_array($value['contact_sub_type_b'], $contactSubType) ||
565 (!$value['contact_sub_type_a'] && !$onlySubTypeRelationTypes)
6a488035
TO
566 )
567 ) {
568 $relationshipType[$key . '_a_b'] = $value["{$column}_a_b"];
569 }
6a488035
TO
570
571 if (((!$value['contact_type_b']) ||
572 $value['contact_type_b'] == $contactType
573 ) &&
574 ((!$value['contact_type_a']) ||
575 (!$otherContactType) ||
576 $value['contact_type_a'] == $otherContactType
577 ) &&
91c0d0d8 578 (in_array($value['contact_sub_type_b'], $contactSubType) ||
579 in_array($value['contact_sub_type_a'], $contactSubType) ||
580 (!$value['contact_sub_type_b'] && !$onlySubTypeRelationTypes)
6a488035
TO
581 )
582 ) {
583 $relationshipType[$key . '_b_a'] = $value["{$column}_b_a"];
584 }
6a488035
TO
585
586 if ($all) {
587 $relationshipType[$key . '_a_b'] = $value["{$column}_a_b"];
588 $relationshipType[$key . '_b_a'] = $value["{$column}_b_a"];
589 }
590 }
591
592 if ($biDirectional) {
593 // lets clean up the data and eliminate all duplicate values
594 // (i.e. the relationship is bi-directional)
595 $relationshipType = array_unique($relationshipType);
596 }
597
598 // sort the relationshipType in ascending order CRM-7736
599 asort($relationshipType);
600 return $relationshipType;
601 }
602
86538308 603 /**
98a06ae0
EM
604 * Delete current employer relationship.
605 *
100fef9d 606 * @param int $id
98a06ae0 607 * @param int $action
86538308
EM
608 *
609 * @return CRM_Contact_DAO_Relationship
610 */
00be9182 611 public static function clearCurrentEmployer($id, $action) {
6a488035
TO
612 $relationship = new CRM_Contact_DAO_Relationship();
613 $relationship->id = $id;
614 $relationship->find(TRUE);
615
616 //to delete relationship between household and individual \
98a06ae0 617 //or between individual and organization
6a488035 618 if (($action & CRM_Core_Action::DISABLE) || ($action & CRM_Core_Action::DELETE)) {
05802b8e
DG
619 $relTypes = CRM_Utils_Array::index(array('name_a_b'), CRM_Core_PseudoConstant::relationshipType('name'));
620 if ($relationship->relationship_type_id == $relTypes['Employee of']['id'] ||
7f3647bc
TO
621 $relationship->relationship_type_id == $relTypes['Household Member of']['id']
622 ) {
05802b8e
DG
623 $sharedContact = new CRM_Contact_DAO_Contact();
624 $sharedContact->id = $relationship->contact_id_a;
625 $sharedContact->find(TRUE);
626
bd655ee6
MV
627 // CRM-15881 UPDATES
628 // changed FROM "...relationship->relationship_type_id == 4..." TO "...relationship->relationship_type_id == 5..."
629 // As the system should be looking for type "employer of" (id 5) and not "sibling of" (id 4)
3421dcee
MV
630 // 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.
631 $employerRelTypeId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType', 'Employee of', 'id', 'name_a_b');
632
633 if ($relationship->relationship_type_id == $employerRelTypeId && $relationship->contact_id_b == $sharedContact->employer_id) {
05802b8e
DG
634 CRM_Contact_BAO_Contact_Utils::clearCurrentEmployer($relationship->contact_id_a);
635 }
3421dcee 636
6a488035
TO
637 }
638 }
7f3647bc 639 return $relationship;
6a488035
TO
640 }
641
642 /**
fe482240 643 * Delete the relationship.
6a488035 644 *
77c5b619
TO
645 * @param int $id
646 * Relationship id.
6a488035
TO
647 *
648 * @return null
6a488035 649 */
00be9182 650 public static function del($id) {
6a488035
TO
651 // delete from relationship table
652 CRM_Utils_Hook::pre('delete', 'Relationship', $id, CRM_Core_DAO::$_nullArray);
653
654 $relationship = self::clearCurrentEmployer($id, CRM_Core_Action::DELETE);
655 if (CRM_Core_Permission::access('CiviMember')) {
656 // create $params array which isrequired to delete memberships
657 // of the related contacts.
658 $params = array(
659 'relationship_type_id' => "{$relationship->relationship_type_id}_a_b",
660 'contact_check' => array($relationship->contact_id_b => 1),
661 );
662
663 $ids = array();
664 // calling relatedMemberships to delete the memberships of
665 // related contacts.
666 self::relatedMemberships($relationship->contact_id_a,
667 $params,
668 $ids,
669 CRM_Core_Action::DELETE,
670 FALSE
671 );
672 }
673
674 $relationship->delete();
675 CRM_Core_Session::setStatus(ts('Selected relationship has been deleted successfully.'), ts('Record Deleted'), 'success');
676
5c208c3f 677 CRM_Utils_Hook::post('delete', 'Relationship', $id, $relationship);
6a488035
TO
678
679 // delete the recently created Relationship
680 $relationshipRecent = array(
681 'id' => $id,
682 'type' => 'Relationship',
683 );
684 CRM_Utils_Recent::del($relationshipRecent);
685
686 return $relationship;
687 }
688
689 /**
98a06ae0 690 * Disable/enable the relationship.
6a488035 691 *
77c5b619
TO
692 * @param int $id
693 * Relationship id.
6a488035 694 *
7a44a255
EM
695 * @param int $action
696 * @param array $params
697 * @param array $ids
698 * @param bool $active
6a488035 699 */
690c56c3 700 public static function disableEnableRelationship($id, $action, $params = array(), $ids = array(), $active = FALSE) {
6a488035 701 $relationship = self::clearCurrentEmployer($id, $action);
25f1372e 702
1b5fad8a 703 if ($id) {
25f1372e 704 // create $params array which is required to delete memberships
6a488035 705 // of the related contacts.
690c56c3 706 if (empty($params)) {
707 $params = array(
708 'relationship_type_id' => "{$relationship->relationship_type_id}_a_b",
709 'contact_check' => array($relationship->contact_id_b => 1),
710 );
711 }
690c56c3 712 $contact_id_a = empty($params['contact_id_a']) ? $relationship->contact_id_a : $params['contact_id_a'];
6a488035
TO
713 // calling relatedMemberships to delete/add the memberships of
714 // related contacts.
715 if ($action & CRM_Core_Action::DISABLE) {
690c56c3 716 CRM_Contact_BAO_Relationship::relatedMemberships($contact_id_a,
6a488035
TO
717 $params,
718 $ids,
719 CRM_Core_Action::DELETE,
690c56c3 720 $active
6a488035
TO
721 );
722 }
723 elseif ($action & CRM_Core_Action::ENABLE) {
91244f1d 724 $ids['contact'] = empty($ids['contact']) ? $contact_id_a : $ids['contact'];
690c56c3 725 CRM_Contact_BAO_Relationship::relatedMemberships($contact_id_a,
6a488035
TO
726 $params,
727 $ids,
690c56c3 728 empty($params['id']) ? CRM_Core_Action::ADD : CRM_Core_Action::UPDATE,
729 $active
6a488035
TO
730 );
731 }
732 }
733 }
734
735 /**
fe482240 736 * Delete the object records that are associated with this contact.
6a488035 737 *
77c5b619
TO
738 * @param int $contactId
739 * Id of the contact to delete.
6a488035 740 */
00be9182 741 public static function deleteContact($contactId) {
6a488035
TO
742 $relationship = new CRM_Contact_DAO_Relationship();
743 $relationship->contact_id_a = $contactId;
744 $relationship->delete();
745
746 $relationship = new CRM_Contact_DAO_Relationship();
747 $relationship->contact_id_b = $contactId;
748 $relationship->delete();
749
750 CRM_Contact_BAO_Household::updatePrimaryContact(NULL, $contactId);
751 }
752
753 /**
fe482240 754 * Get the other contact in a relationship.
6a488035 755 *
77c5b619
TO
756 * @param int $id
757 * Relationship id.
6a488035
TO
758 *
759 * $returns returns the contact ids in the realtionship
77b97be7
EM
760 *
761 * @return \CRM_Contact_DAO_Relationship
6a488035 762 */
eff45dce 763 public static function getRelationshipByID($id) {
6a488035
TO
764 $relationship = new CRM_Contact_DAO_Relationship();
765
766 $relationship->id = $id;
767 $relationship->selectAdd();
768 $relationship->selectAdd('contact_id_a, contact_id_b');
769 $relationship->find(TRUE);
770
771 return $relationship;
772 }
773
774 /**
fe482240 775 * Check if the relationship type selected between two contacts is correct.
6a488035 776 *
77c5b619
TO
777 * @param int $contact_a
778 * 1st contact id.
779 * @param int $contact_b
780 * 2nd contact id.
781 * @param int $relationshipTypeId
782 * Relationship type id.
6a488035 783 *
c301f76e 784 * @return bool
a6c01b45 785 * true if it is valid relationship else false
6a488035 786 */
00be9182 787 public static function checkRelationshipType($contact_a, $contact_b, $relationshipTypeId) {
6a488035
TO
788 $relationshipType = new CRM_Contact_DAO_RelationshipType();
789 $relationshipType->id = $relationshipTypeId;
790 $relationshipType->selectAdd();
791 $relationshipType->selectAdd('contact_type_a, contact_type_b, contact_sub_type_a, contact_sub_type_b');
792 if ($relationshipType->find(TRUE)) {
793 $contact_type_a = CRM_Contact_BAO_Contact::getContactType($contact_a);
794 $contact_type_b = CRM_Contact_BAO_Contact::getContactType($contact_b);
795
796 $contact_sub_type_a = CRM_Contact_BAO_Contact::getContactSubType($contact_a);
797 $contact_sub_type_b = CRM_Contact_BAO_Contact::getContactSubType($contact_b);
798
799 if (((!$relationshipType->contact_type_a) || ($relationshipType->contact_type_a == $contact_type_a)) &&
800 ((!$relationshipType->contact_type_b) || ($relationshipType->contact_type_b == $contact_type_b)) &&
801 ((!$relationshipType->contact_sub_type_a) || (in_array($relationshipType->contact_sub_type_a,
7f3647bc
TO
802 $contact_sub_type_a
803 ))) &&
6a488035 804 ((!$relationshipType->contact_sub_type_b) || (in_array($relationshipType->contact_sub_type_b,
7f3647bc
TO
805 $contact_sub_type_b
806 )))
6a488035
TO
807 ) {
808 return TRUE;
809 }
810 else {
811 return FALSE;
812 }
813 }
814 return FALSE;
815 }
816
817 /**
fe482240 818 * This function does the validtion for valid relationship.
6a488035 819 *
77c5b619
TO
820 * @param array $params
821 * This array contains the values there are subitted by the form.
822 * @param array $ids
823 * The array that holds all the db ids.
824 * @param int $contactId
825 * This is contact id for adding relationship.
6a488035 826 *
2a6da8d7 827 * @return string
6a488035 828 */
7ecf6275 829 public static function checkValidRelationship($params, $ids, $contactId) {
6a488035 830 $errors = '';
6a488035
TO
831 // function to check if the relationship selected is correct
832 // i.e. employer relationship can exit between Individual and Organization (not between Individual and Individual)
c4dcea5b
EM
833 if (!CRM_Contact_BAO_Relationship::checkRelationshipType($params['contact_id_a'], $params['contact_id_b'],
834 $params['relationship_type_id'])) {
6a488035
TO
835 $errors = 'Please select valid relationship between these two contacts.';
836 }
837 return $errors;
838 }
839
840 /**
fe482240 841 * This function checks for duplicate relationship.
6a488035 842 *
77c5b619
TO
843 * @param array $params
844 * (reference ) an assoc array of name/value pairs.
845 * @param int $id
846 * This the id of the contact whom we are adding relationship.
847 * @param int $contactId
848 * This is contact id for adding relationship.
849 * @param int $relationshipId
850 * This is relationship id for the contact.
6a488035 851 *
c301f76e 852 * @return bool
a6c01b45 853 * true if record exists else false
6a488035 854 */
00be9182 855 public static function checkDuplicateRelationship(&$params, $id, $contactId = 0, $relationshipId = 0) {
6a488035 856 $relationshipTypeId = CRM_Utils_Array::value('relationship_type_id', $params);
decc60a0 857 list($type) = explode('_', $relationshipTypeId);
6a488035 858
f0fc26a5
DL
859 $queryString = "
860SELECT id
861FROM civicrm_relationship
862WHERE relationship_type_id = " . CRM_Utils_Type::escape($type, 'Integer');
6a488035
TO
863
864 /*
e70a7fc0
TO
865 * CRM-11792 - date fields from API are in ISO format, but this function
866 * supports date arrays BAO has increasingly standardised to ISO format
867 * so I believe this function should support ISO rather than make API
868 * format it - however, need to support array format for now to avoid breakage
2da59b29 869 * @ time of writing this function is called from Relationship::legacyCreateMultiple (twice)
e70a7fc0
TO
870 * CRM_BAO_Contact_Utils::clearCurrentEmployer (seemingly without dates)
871 * CRM_Contact_Form_Task_AddToOrganization::postProcess &
872 * CRM_Contact_Form_Task_AddToHousehold::postProcess
873 * (I don't think the last 2 support dates but not sure
874 */
6a488035
TO
875
876 $dateFields = array('end_date', 'start_date');
9b873358 877 foreach ($dateFields as $dateField) {
22e263ad 878 if (array_key_exists($dateField, $params)) {
9b873358 879 if (empty($params[$dateField]) || $params[$dateField] == 'null') {
f0fc26a5
DL
880 //this is most likely coming from an api call & probably loaded
881 // from the DB to deal with some of the
882 // other myriad of excessive checks still in place both in
883 // the api & the create functions
6a488035
TO
884 $queryString .= " AND $dateField IS NULL";
885 continue;
886 }
9b873358 887 elseif (is_array($params[$dateField])) {
f0fc26a5
DL
888 $queryString .= " AND $dateField = " .
889 CRM_Utils_Type::escape(CRM_Utils_Date::format($params[$dateField]), 'Date');
6a488035 890 }
f0fc26a5
DL
891 else {
892 $queryString .= " AND $dateField = " .
893 CRM_Utils_Type::escape($params[$dateField], 'Date');
6a488035
TO
894 }
895 }
896 }
897
898 $queryString .=
899 " AND ( ( contact_id_a = " . CRM_Utils_Type::escape($id, 'Integer') .
900 " AND contact_id_b = " . CRM_Utils_Type::escape($contactId, 'Integer') .
901 " ) OR ( contact_id_a = " . CRM_Utils_Type::escape($contactId, 'Integer') .
902 " AND contact_id_b = " . CRM_Utils_Type::escape($id, 'Integer') . " ) ) ";
903
904 //if caseId is provided, include it duplicate checking.
905 if ($caseId = CRM_Utils_Array::value('case_id', $params)) {
906 $queryString .= " AND case_id = " . CRM_Utils_Type::escape($caseId, 'Integer');
907 }
908
909 if ($relationshipId) {
910 $queryString .= " AND id !=" . CRM_Utils_Type::escape($relationshipId, 'Integer');
911 }
912
913 $relationship = new CRM_Contact_BAO_Relationship();
914 $relationship->query($queryString);
915 $relationship->fetch();
916 $relationship->free();
917 return ($relationship->id) ? TRUE : FALSE;
918 }
919
920 /**
fe482240 921 * Update the is_active flag in the db.
6a488035 922 *
77c5b619
TO
923 * @param int $id
924 * Id of the database record.
925 * @param bool $is_active
926 * Value we want to set the is_active field.
6a488035 927 *
77b97be7 928 * @throws CiviCRM_API3_Exception
a6c01b45
CW
929 * @return Object
930 * DAO object on success, null otherwise
6a488035 931 */
00be9182 932 public static function setIsActive($id, $is_active) {
49f8272d
E
933 // as both the create & add functions have a bunch of logic in them that
934 // doesn't seem to cope with a normal update we will call the api which
935 // has tested handling for this
936 // however, a longer term solution would be to simplify the add, create & api functions
937 // to be more standard. It is debatable @ that point whether it's better to call the BAO
938 // direct as the api is more tested.
80109b10 939 $result = civicrm_api('relationship', 'create', array(
940 'id' => $id,
941 'is_active' => $is_active,
942 'version' => 3,
943 ));
944
945 if (is_array($result) && !empty($result['is_error']) && $result['error_message'] != 'Relationship already exists') {
946 throw new CiviCRM_API3_Exception($result['error_message'], CRM_Utils_Array::value('error_code', $result, 'undefined'), $result);
947 }
49f8272d 948
6a488035
TO
949 return TRUE;
950 }
951
952 /**
98a06ae0 953 * Fetch a relationship object and store the values in the values array.
6a488035 954 *
77c5b619
TO
955 * @param array $params
956 * Input parameters to find object.
957 * @param array $values
958 * Output values of the object.
6a488035 959 *
a6c01b45
CW
960 * @return array
961 * (reference) the values that could be potentially assigned to smarty
6a488035 962 */
00be9182 963 public static function &getValues(&$params, &$values) {
6a488035
TO
964 if (empty($params)) {
965 return NULL;
966 }
967 $v = array();
968
969 // get the specific number of relationship or all relationships.
a7488080 970 if (!empty($params['numRelationship'])) {
6a488035
TO
971 $v['data'] = &CRM_Contact_BAO_Relationship::getRelationship($params['contact_id'], NULL, $params['numRelationship']);
972 }
973 else {
974 $v['data'] = CRM_Contact_BAO_Relationship::getRelationship($params['contact_id']);
975 }
976
977 // get the total count of relationships
c23fcc07 978 $v['totalCount'] = count($v['data']);
6a488035
TO
979
980 $values['relationship']['data'] = &$v['data'];
981 $values['relationship']['totalCount'] = &$v['totalCount'];
982
983 return $v;
984 }
985
986 /**
fe482240 987 * Helper function to form the sql for relationship retrieval.
6a488035 988 *
77c5b619
TO
989 * @param int $contactId
990 * Contact id.
991 * @param int $status
992 * (check const at top of file).
993 * @param int $numRelationship
994 * No of relationships to display (limit).
995 * @param int $count
996 * Get the no of relationships.
6a488035 997 * $param int $relationshipId relationship id
100fef9d 998 * @param int $relationshipId
77c5b619
TO
999 * @param string $direction
1000 * The direction we are interested in a_b or b_a.
1001 * @param array $params
1002 * Array of extra values including relationship_type_id per api spec.
6a488035 1003 *
77b97be7 1004 * @return array
76e7a76c 1005 * [select, from, where]
6a488035 1006 */
00be9182 1007 public static function makeURLClause($contactId, $status, $numRelationship, $count, $relationshipId, $direction, $params = array()) {
6a488035
TO
1008 $select = $from = $where = '';
1009
1010 $select = '( ';
1011 if ($count) {
1012 if ($direction == 'a_b') {
1013 $select .= ' SELECT count(DISTINCT civicrm_relationship.id) as cnt1, 0 as cnt2 ';
1014 }
1015 else {
1016 $select .= ' SELECT 0 as cnt1, count(DISTINCT civicrm_relationship.id) as cnt2 ';
1017 }
1018 }
1019 else {
1020 $select .= ' SELECT civicrm_relationship.id as civicrm_relationship_id,
1021 civicrm_contact.sort_name as sort_name,
1022 civicrm_contact.display_name as display_name,
1023 civicrm_contact.job_title as job_title,
1024 civicrm_contact.employer_id as employer_id,
1025 civicrm_contact.organization_name as organization_name,
1026 civicrm_address.street_address as street_address,
1027 civicrm_address.city as city,
1028 civicrm_address.postal_code as postal_code,
1029 civicrm_state_province.abbreviation as state,
1030 civicrm_country.name as country,
1031 civicrm_email.email as email,
c6c691a5 1032 civicrm_contact.contact_type as contact_type,
6a488035
TO
1033 civicrm_phone.phone as phone,
1034 civicrm_contact.id as civicrm_contact_id,
6a488035
TO
1035 civicrm_relationship.contact_id_b as contact_id_b,
1036 civicrm_relationship.contact_id_a as contact_id_a,
1037 civicrm_relationship_type.id as civicrm_relationship_type_id,
1038 civicrm_relationship.start_date as start_date,
1039 civicrm_relationship.end_date as end_date,
1040 civicrm_relationship.description as description,
1041 civicrm_relationship.is_active as is_active,
1042 civicrm_relationship.is_permission_a_b as is_permission_a_b,
1043 civicrm_relationship.is_permission_b_a as is_permission_b_a,
1044 civicrm_relationship.case_id as case_id';
1045
1046 if ($direction == 'a_b') {
1047 $select .= ', civicrm_relationship_type.label_a_b as label_a_b,
c6c691a5 1048 civicrm_relationship_type.label_b_a as relation ';
6a488035
TO
1049 }
1050 else {
1051 $select .= ', civicrm_relationship_type.label_a_b as label_a_b,
c6c691a5 1052 civicrm_relationship_type.label_a_b as relation ';
6a488035
TO
1053 }
1054 }
1055
1056 $from = "
1057 FROM civicrm_relationship
1058INNER JOIN civicrm_relationship_type ON ( civicrm_relationship.relationship_type_id = civicrm_relationship_type.id )
1059INNER JOIN civicrm_contact ";
1060 if ($direction == 'a_b') {
1061 $from .= 'ON ( civicrm_contact.id = civicrm_relationship.contact_id_a ) ';
1062 }
1063 else {
1064 $from .= 'ON ( civicrm_contact.id = civicrm_relationship.contact_id_b ) ';
1065 }
1066 $from .= "
1067LEFT JOIN civicrm_address ON (civicrm_address.contact_id = civicrm_contact.id AND civicrm_address.is_primary = 1)
1068LEFT JOIN civicrm_phone ON (civicrm_phone.contact_id = civicrm_contact.id AND civicrm_phone.is_primary = 1)
1069LEFT JOIN civicrm_email ON (civicrm_email.contact_id = civicrm_contact.id AND civicrm_email.is_primary = 1)
1070LEFT JOIN civicrm_state_province ON (civicrm_address.state_province_id = civicrm_state_province.id)
1071LEFT JOIN civicrm_country ON (civicrm_address.country_id = civicrm_country.id)
1072";
1073 $where = 'WHERE ( 1 )';
1074 if ($contactId) {
1075 if ($direction == 'a_b') {
1076 $where .= ' AND civicrm_relationship.contact_id_b = ' . CRM_Utils_Type::escape($contactId, 'Positive');
1077 }
1078 else {
075fa74e 1079 $where .= ' AND civicrm_relationship.contact_id_a = ' . CRM_Utils_Type::escape($contactId, 'Positive') . '
1080 AND civicrm_relationship.contact_id_a != civicrm_relationship.contact_id_b ';
6a488035
TO
1081 }
1082 }
1083 if ($relationshipId) {
1084 $where .= ' AND civicrm_relationship.id = ' . CRM_Utils_Type::escape($relationshipId, 'Positive');
1085 }
1086
1087 $date = date('Y-m-d');
1088 if ($status == self::PAST) {
1089 //this case for showing past relationship
1090 $where .= ' AND civicrm_relationship.is_active = 1 ';
1091 $where .= " AND civicrm_relationship.end_date < '" . $date . "'";
1092 }
1093 elseif ($status == self::DISABLED) {
1094 // this case for showing disabled relationship
1095 $where .= ' AND civicrm_relationship.is_active = 0 ';
1096 }
1097 elseif ($status == self::CURRENT) {
1098 //this case for showing current relationship
1099 $where .= ' AND civicrm_relationship.is_active = 1 ';
1100 $where .= " AND (civicrm_relationship.end_date >= '" . $date . "' OR civicrm_relationship.end_date IS NULL) ";
1101 }
1102 elseif ($status == self::INACTIVE) {
1103 //this case for showing inactive relationships
1104 $where .= " AND (civicrm_relationship.end_date < '" . $date . "'";
1105 $where .= ' OR civicrm_relationship.is_active = 0 )';
1106 }
1107
1108 // CRM-6181
1109 $where .= ' AND civicrm_contact.is_deleted = 0';
22e263ad 1110 if (!empty($params['membership_type_id']) && empty($params['relationship_type_id'])) {
c9c41397 1111 $where .= self::membershipTypeToRelationshipTypes($params, $direction);
1112 }
22e263ad
TO
1113 if (!empty($params['relationship_type_id'])) {
1114 if (is_array($params['relationship_type_id'])) {
7f3647bc 1115 $where .= " AND " . CRM_Core_DAO::createSQLFilter('relationship_type_id', $params['relationship_type_id'], 'Integer');
75638074 1116 }
1117 else {
1118 $where .= ' AND relationship_type_id = ' . CRM_Utils_Type::escape($params['relationship_type_id'], 'Positive');
1119 }
1120 }
6a488035
TO
1121 if ($direction == 'a_b') {
1122 $where .= ' ) UNION ';
1123 }
1124 else {
1125 $where .= ' ) ';
1126 }
1127
1128 return array($select, $from, $where);
1129 }
1130
1131 /**
fe482240 1132 * Get a list of relationships.
6a488035 1133 *
77c5b619
TO
1134 * @param int $contactId
1135 * Contact id.
1136 * @param int $status
1137 * 1: Past 2: Disabled 3: Current.
1138 * @param int $numRelationship
1139 * No of relationships to display (limit).
1140 * @param int $count
1141 * Get the no of relationships.
77b97be7 1142 * @param int $relationshipId
76e7a76c
CW
1143 * @param array $links
1144 * the list of links to display
1145 * @param int $permissionMask
1146 * the permission mask to be applied for the actions
77b97be7 1147 * @param bool $permissionedContact
76e7a76c 1148 * to return only permissioned Contact
77b97be7
EM
1149 * @param array $params
1150 *
1151 * @return array|int
76e7a76c 1152 * relationship records
6a488035 1153 */
c301f76e 1154 public static function getRelationship(
51ccfbbe 1155 $contactId = NULL,
7f3647bc
TO
1156 $status = 0, $numRelationship = 0,
1157 $count = 0, $relationshipId = 0,
1158 $links = NULL, $permissionMask = NULL,
75638074 1159 $permissionedContact = FALSE,
1160 $params = array()
6a488035
TO
1161 ) {
1162 $values = array();
1163 if (!$contactId && !$relationshipId) {
1164 return $values;
1165 }
1166
1167 list($select1, $from1, $where1) = self::makeURLClause($contactId, $status, $numRelationship,
75638074 1168 $count, $relationshipId, 'a_b', $params
6a488035
TO
1169 );
1170 list($select2, $from2, $where2) = self::makeURLClause($contactId, $status, $numRelationship,
75638074 1171 $count, $relationshipId, 'b_a', $params
6a488035
TO
1172 );
1173
1174 $order = $limit = '';
1175 if (!$count) {
40458f6c 1176 if (empty($params['sort'])) {
1177 $order = ' ORDER BY civicrm_relationship_type_id, sort_name ';
1178 }
1179 else {
1180 $order = " ORDER BY {$params['sort']} ";
1181 }
1182
1183 $offset = 0;
630d1aaa 1184 if (!empty($params['offset']) && $params['offset'] > 0) {
40458f6c 1185 $offset = $params['offset'];
1186 }
6a488035
TO
1187
1188 if ($numRelationship) {
40458f6c 1189 $limit = " LIMIT {$offset}, $numRelationship";
6a488035
TO
1190 }
1191 }
1192
1193 // building the query string
6a488035
TO
1194 $queryString = $select1 . $from1 . $where1 . $select2 . $from2 . $where2 . $order . $limit;
1195
1196 $relationship = new CRM_Contact_DAO_Relationship();
1197
1198 $relationship->query($queryString);
1199 $row = array();
1200 if ($count) {
1201 $relationshipCount = 0;
1202 while ($relationship->fetch()) {
1203 $relationshipCount += $relationship->cnt1 + $relationship->cnt2;
1204 }
1205 return $relationshipCount;
1206 }
1207 else {
1208
1209 $mask = NULL;
1210 if ($status != self::INACTIVE) {
1211 if ($links) {
1212 $mask = array_sum(array_keys($links));
1213 if ($mask & CRM_Core_Action::DISABLE) {
1214 $mask -= CRM_Core_Action::DISABLE;
1215 }
1216 if ($mask & CRM_Core_Action::ENABLE) {
1217 $mask -= CRM_Core_Action::ENABLE;
1218 }
1219
1220 if ($status == self::CURRENT) {
1221 $mask |= CRM_Core_Action::DISABLE;
1222 }
1223 elseif ($status == self::DISABLED) {
1224 $mask |= CRM_Core_Action::ENABLE;
1225 }
1226 $mask = $mask & $permissionMask;
1227 }
1228 }
c23fcc07 1229
6a488035
TO
1230 while ($relationship->fetch()) {
1231 $rid = $relationship->civicrm_relationship_id;
1232 $cid = $relationship->civicrm_contact_id;
df124d3a 1233 if (($permissionedContact &&
1234 (!CRM_Contact_BAO_Contact_Permission::relationship($cid, $contactId))
1235 ) ||
1236 (!CRM_Contact_BAO_Contact_Permission::allow($cid))
6a488035
TO
1237 ) {
1238 continue;
1239 }
1240 $values[$rid]['id'] = $rid;
1241 $values[$rid]['cid'] = $cid;
1242 $values[$rid]['contact_id_a'] = $relationship->contact_id_a;
1243 $values[$rid]['contact_id_b'] = $relationship->contact_id_b;
c6c691a5 1244 $values[$rid]['contact_type'] = $relationship->contact_type;
6a488035
TO
1245 $values[$rid]['relationship_type_id'] = $relationship->civicrm_relationship_type_id;
1246 $values[$rid]['relation'] = $relationship->relation;
1247 $values[$rid]['name'] = $relationship->sort_name;
1248 $values[$rid]['display_name'] = $relationship->display_name;
1249 $values[$rid]['job_title'] = $relationship->job_title;
1250 $values[$rid]['email'] = $relationship->email;
1251 $values[$rid]['phone'] = $relationship->phone;
1252 $values[$rid]['employer_id'] = $relationship->employer_id;
1253 $values[$rid]['organization_name'] = $relationship->organization_name;
1254 $values[$rid]['country'] = $relationship->country;
1255 $values[$rid]['city'] = $relationship->city;
1256 $values[$rid]['state'] = $relationship->state;
1257 $values[$rid]['start_date'] = $relationship->start_date;
1258 $values[$rid]['end_date'] = $relationship->end_date;
1259 $values[$rid]['description'] = $relationship->description;
1260 $values[$rid]['is_active'] = $relationship->is_active;
1261 $values[$rid]['is_permission_a_b'] = $relationship->is_permission_a_b;
1262 $values[$rid]['is_permission_b_a'] = $relationship->is_permission_b_a;
1263 $values[$rid]['case_id'] = $relationship->case_id;
1264
1265 if ($status) {
1266 $values[$rid]['status'] = $status;
1267 }
1268
1269 $values[$rid]['civicrm_relationship_type_id'] = $relationship->civicrm_relationship_type_id;
1270
1271 if ($relationship->contact_id_a == $contactId) {
1272 $values[$rid]['rtype'] = 'a_b';
1273 }
1274 else {
1275 $values[$rid]['rtype'] = 'b_a';
1276 }
1277
1278 if ($links) {
1279 $replace = array(
1280 'id' => $rid,
1281 'rtype' => $values[$rid]['rtype'],
1282 'cid' => $contactId,
1283 'cbid' => $values[$rid]['cid'],
1284 'caseid' => $values[$rid]['case_id'],
1285 'clientid' => $contactId,
1286 );
1287
1288 if ($status == self::INACTIVE) {
1289 // setting links for inactive relationships
1290 $mask = array_sum(array_keys($links));
1291 if (!$values[$rid]['is_active']) {
1292 $mask -= CRM_Core_Action::DISABLE;
1293 }
1294 else {
1295 $mask -= CRM_Core_Action::ENABLE;
1296 $mask -= CRM_Core_Action::DISABLE;
1297 }
7060f0c3 1298 $mask = $mask & $permissionMask;
6a488035
TO
1299 }
1300
1301 // Give access to manage case link by copying to MAX_ACTION index temporarily, depending on case permission of user.
1302 if ($values[$rid]['case_id']) {
1303 // Borrowed logic from CRM_Case_Page_Tab
1304 $hasCaseAccess = FALSE;
1305 if (CRM_Core_Permission::check('access all cases and activities')) {
1306 $hasCaseAccess = TRUE;
1307 }
1308 else {
1309 $userCases = CRM_Case_BAO_Case::getCases(FALSE);
1310 if (array_key_exists($values[$rid]['case_id'], $userCases)) {
1311 $hasCaseAccess = TRUE;
1312 }
1313 }
1314
1315 if ($hasCaseAccess) {
1316 // give access by copying to MAX_ACTION temporarily, otherwise leave at NONE which won't display
1317 $links[CRM_Core_Action::MAX_ACTION] = $links[CRM_Core_Action::NONE];
1318 $links[CRM_Core_Action::MAX_ACTION]['name'] = ts('Manage Case #%1', array(1 => $values[$rid]['case_id']));
f89f2102 1319 $links[CRM_Core_Action::MAX_ACTION]['class'] = 'no-popup';
6a488035
TO
1320
1321 // Also make sure we have the right client cid since can get here from multiple relationship tabs.
1322 if ($values[$rid]['rtype'] == 'b_a') {
1323 $replace['clientid'] = $values[$rid]['cid'];
1324 }
1325 }
1326 }
1327
87dab4a4 1328 $values[$rid]['action'] = CRM_Core_Action::formLink(
1cfa04c4
EM
1329 $links,
1330 $mask,
87dab4a4
AH
1331 $replace,
1332 ts('more'),
1333 FALSE,
1334 'relationship.selector.row',
1335 'Relationship',
1336 $rid);
6a488035
TO
1337 unset($links[CRM_Core_Action::MAX_ACTION]);
1338 }
1339 }
1340
1341 $relationship->free();
1342 return $values;
1343 }
1344 }
1345
1346 /**
100fef9d 1347 * Get get list of relationship type based on the target contact type.
6a488035 1348 *
77c5b619
TO
1349 * @param string $targetContactType
1350 * It's valid contact tpye(may be Individual , Organization , Household).
6a488035 1351 *
a6c01b45
CW
1352 * @return array
1353 * array reference of all relationship types with context to current contact type .
6a488035 1354 */
ee17a760 1355 static public function getRelationType($targetContactType) {
6a488035
TO
1356 $relationshipType = array();
1357 $allRelationshipType = CRM_Core_PseudoConstant::relationshipType();
1358
1359 foreach ($allRelationshipType as $key => $type) {
1360 if ($type['contact_type_b'] == $targetContactType) {
1361 $relationshipType[$key . '_a_b'] = $type['label_a_b'];
1362 }
1363 }
1364
1365 return $relationshipType;
1366 }
1367
1368 /**
100fef9d 1369 * Create / update / delete membership for related contacts.
6a488035
TO
1370 *
1371 * This function will create/update/delete membership for related
1372 * contact based on 1) contact have active membership 2) that
1373 * membership is is extedned by the same relationship type to that
1374 * of the existing relationship.
1375 *
5a4f6742
CW
1376 * @param int $contactId
1377 * contact id.
1378 * @param array $params
1379 * array of values submitted by POST.
1380 * @param array $ids
1381 * array of ids.
bfa4da96 1382 * @param \const|int $action which action called this function
6a488035 1383 *
dd244018 1384 * @param bool $active
6a488035 1385 *
bfa4da96 1386 * @throws \CRM_Core_Exception
6a488035 1387 */
00be9182 1388 public static function relatedMemberships($contactId, &$params, $ids, $action = CRM_Core_Action::ADD, $active = TRUE) {
6a488035 1389 // Check the end date and set the status of the relationship
decc60a0 1390 // accordingly.
6a488035 1391 $status = self::CURRENT;
04ad3598 1392 $targetContact = $targetContact = CRM_Utils_Array::value('contact_check', $params, array());
6a488035
TO
1393
1394 if (!empty($params['end_date'])) {
1395 $endDate = CRM_Utils_Date::setDateDefaults(CRM_Utils_Date::format($params['end_date']), NULL, 'Ymd');
1396 $today = date('Ymd');
1397
1398 if ($today > $endDate) {
1399 $status = self::PAST;
1400 }
1401 }
1402
1403 if (($action & CRM_Core_Action::ADD) &&
1404 ($status & self::PAST)
1405 ) {
1406 // if relationship is PAST and action is ADD, no qustion
1407 // of creating RELATED membership and return back to
1408 // calling method
1409 return;
1410 }
1411
1412 $rel = explode('_', $params['relationship_type_id']);
1413
7f3647bc 1414 $relTypeId = $rel[0];
decc60a0
EM
1415 if (!empty($rel[1])) {
1416 $relDirection = "_{$rel[1]}_{$rel[2]}";
1417 }
1418 else {
1419 // this call is coming from somewhere where the direction was resolved early on (e.g an api call)
1420 // so we can assume _a_b
1421 $relDirection = "_a_b";
04ad3598 1422 $targetContact = array($params['contact_id_b'] => 1);
decc60a0 1423 }
04ad3598 1424
6a488035
TO
1425 if (($action & CRM_Core_Action::ADD) ||
1426 ($action & CRM_Core_Action::DELETE)
1427 ) {
1428 $contact = $contactId;
6a488035
TO
1429 }
1430 elseif ($action & CRM_Core_Action::UPDATE) {
1431 $contact = $ids['contact'];
1432 $targetContact = array($ids['contactTarget'] => 1);
1433 }
1434
1435 // Build the 'values' array for
1436 // 1. ContactA
1437 // 2. ContactB
1438 // This will allow us to check if either of the contacts in
1439 // relationship have active memberships.
1440
1441 $values = array();
1442
1443 // 1. ContactA
1444 $values[$contact] = array(
1445 'relatedContacts' => $targetContact,
1446 'relationshipTypeId' => $relTypeId,
1447 'relationshipTypeDirection' => $relDirection,
1448 );
1449 // 2. ContactB
1450 if (!empty($targetContact)) {
1451 foreach ($targetContact as $cid => $donCare) {
1452 $values[$cid] = array(
1453 'relatedContacts' => array($contact => 1),
1454 'relationshipTypeId' => $relTypeId,
1455 );
1456
1457 $relTypeParams = array('id' => $relTypeId);
1458 $relTypeValues = array();
1459 CRM_Contact_BAO_RelationshipType::retrieve($relTypeParams, $relTypeValues);
1460
1461 if (CRM_Utils_Array::value('name_a_b', $relTypeValues) == CRM_Utils_Array::value('name_b_a', $relTypeValues)) {
1462 $values[$cid]['relationshipTypeDirection'] = '_a_b';
1463 }
1464 else {
1465 $values[$cid]['relationshipTypeDirection'] = ($relDirection == '_a_b') ? '_b_a' : '_a_b';
1466 }
1467 }
1468 }
1469
bd655ee6
MV
1470 // CRM-15829 UPDATES
1471 // If we're looking for active memberships we must consider pending (id: 5) ones too.
1472 // 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
1473 // 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.
1474 $pendingStatusId = array_search('Pending', CRM_Member_PseudoConstant::membershipStatus());
bd655ee6
MV
1475
1476 $query = 'SELECT * FROM `civicrm_membership_status`';
1477 if ($active) {
3421dcee 1478 $query .= 'WHERE `is_current_member` = 1 OR `id` = %1 ';
bd655ee6
MV
1479 }
1480
3421dcee
MV
1481 $params[1] = array($pendingStatusId, 'String');
1482 $dao = CRM_Core_DAO::executeQuery($query, $params);
1483
bd655ee6
MV
1484 while ($dao->fetch()) {
1485 $membershipStatusRecordIds[$dao->id] = $dao->id;
1486 }
1487
6a488035
TO
1488 // Now get the active memberships for all the contacts.
1489 // If contact have any valid membership(s), then add it to
1490 // 'values' array.
1491 foreach ($values as $cid => $subValues) {
1492 $memParams = array('contact_id' => $cid);
1493 $memberships = array();
1494
bd655ee6
MV
1495 // CRM-15829 UPDATES
1496 // 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.
1497 CRM_Member_BAO_Membership::getValues($memParams, $memberships, FALSE, TRUE);
1498
1499 // CRM-15829 UPDATES
1500 // filter out the memberships returned by CRM_Member_BAO_Membership::getValues based on the status IDs fetched on line ~1462
1501 foreach ($memberships as $key => $membership) {
1502
1503 if (!isset($memberships[$key]['status_id'])) {
1504 continue;
1505 }
1506
1507 $membershipStatusId = $memberships[$key]['status_id'];
1508 if (!isset($membershipStatusRecordIds[$membershipStatusId])) {
1509 unset($memberships[$key]);
1510 }
1511 }
6a488035
TO
1512
1513 if (empty($memberships)) {
1514 continue;
1515 }
1516
f1e48e7f 1517 //get ownerMembershipIds for related Membership
1518 //this is to handle memberships being deleted and recreated
1519 if (!empty($memberships['owner_membership_ids'])) {
91b00626 1520 $ownerMemIds[$cid] = $memberships['owner_membership_ids'];
f1e48e7f 1521 unset($memberships['owner_membership_ids']);
1522 }
1523
6a488035
TO
1524 $values[$cid]['memberships'] = $memberships;
1525 }
1526 $deceasedStatusId = array_search('Deceased', CRM_Member_PseudoConstant::membershipStatus());
1527
1528 // done with 'values' array.
1529 // Finally add / edit / delete memberships for the related contacts
ee17a760 1530
6a488035
TO
1531 foreach ($values as $cid => $details) {
1532 if (!array_key_exists('memberships', $details)) {
1533 continue;
1534 }
1535
25f1372e 1536 $relatedContacts = array_keys(CRM_Utils_Array::value('relatedContacts', $details, array()));
ee17a760 1537 $mainRelatedContactId = reset($relatedContacts);
6a488035
TO
1538
1539 foreach ($details['memberships'] as $membershipId => $membershipValues) {
1540 $relTypeIds = array();
1541 if ($action & CRM_Core_Action::DELETE) {
1542 // Delete memberships of the related contacts only if relationship type exists for membership type
1543 $query = "
1544SELECT relationship_type_id, relationship_direction
1545 FROM civicrm_membership_type
1546 WHERE id = {$membershipValues['membership_type_id']}";
1547 $dao = CRM_Core_DAO::executeQuery($query);
1548 $relTypeDirs = array();
1549 while ($dao->fetch()) {
1550 $relTypeId = $dao->relationship_type_id;
1551 $relDirection = $dao->relationship_direction;
1552 }
1553 $relTypeIds = explode(CRM_Core_DAO::VALUE_SEPARATOR, $relTypeId);
4d88abcc 1554 if (in_array($values[$cid]['relationshipTypeId'], $relTypeIds
1555 //CRM-16300 check if owner membership exist for related membership
1556 ) && !empty($membershipValues['owner_membership_id']) && !empty($values[$mainRelatedContactId]['memberships'][$membershipValues['owner_membership_id']])) {
690c56c3 1557 CRM_Member_BAO_Membership::deleteRelatedMemberships($membershipValues['owner_membership_id'], $membershipValues['membership_contact_id']);
6a488035
TO
1558 }
1559 continue;
1560 }
1561 if (($action & CRM_Core_Action::UPDATE) &&
1562 ($status & self::PAST) &&
1563 ($membershipValues['owner_membership_id'])
1564 ) {
1565 // If relationship is PAST and action is UPDATE
1566 // then delete the RELATED membership
1567 CRM_Member_BAO_Membership::deleteRelatedMemberships($membershipValues['owner_membership_id'],
1568 $membershipValues['membership_contact_id']
1569 );
1570 continue;
1571 }
1572
1573 // add / edit the memberships for related
1574 // contacts.
1575
1576 // Get the Membership Type Details.
1577 $membershipType = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($membershipValues['membership_type_id']);
1578 // Check if contact's relationship type exists in membership type
1579 $relTypeDirs = array();
a7488080 1580 if (!empty($membershipType['relationship_type_id'])) {
6a488035
TO
1581 $relTypeIds = explode(CRM_Core_DAO::VALUE_SEPARATOR, $membershipType['relationship_type_id']);
1582 }
a7488080 1583 if (!empty($membershipType['relationship_direction'])) {
6a488035
TO
1584 $relDirections = explode(CRM_Core_DAO::VALUE_SEPARATOR, $membershipType['relationship_direction']);
1585 }
1586 foreach ($relTypeIds as $key => $value) {
1587 $relTypeDirs[] = $value . '_' . $relDirections[$key];
1588 }
1589 $relTypeDir = $details['relationshipTypeId'] . $details['relationshipTypeDirection'];
1590 if (in_array($relTypeDir, $relTypeDirs)) {
1591 // Check if relationship being created/updated is
1592 // similar to that of membership type's
1593 // relationship.
1594
1595 $membershipValues['owner_membership_id'] = $membershipId;
1596 unset($membershipValues['id']);
1597 unset($membershipValues['membership_contact_id']);
1598 unset($membershipValues['contact_id']);
1599 unset($membershipValues['membership_id']);
1600 foreach ($details['relatedContacts'] as $relatedContactId => $donCare) {
1601 $membershipValues['contact_id'] = $relatedContactId;
1602 if ($deceasedStatusId &&
1603 CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $relatedContactId, 'is_deceased')
1604 ) {
1605 $membershipValues['status_id'] = $deceasedStatusId;
1606 $membershipValues['skipStatusCal'] = TRUE;
1607 }
1608 foreach (array(
7f3647bc
TO
1609 'join_date',
1610 'start_date',
21dfd5f5 1611 'end_date',
7f3647bc 1612 ) as $dateField) {
a7488080 1613 if (!empty($membershipValues[$dateField])) {
6a488035
TO
1614 $membershipValues[$dateField] = CRM_Utils_Date::processDate($membershipValues[$dateField]);
1615 }
1616 }
1617
1618 if ($action & CRM_Core_Action::UPDATE) {
eec8d770 1619 //if updated relationship is already related to contact don't delete existing inherited membership
4d88abcc 1620 if (in_array($relTypeId, $relTypeIds
f1e48e7f 1621 ) && !empty($values[$relatedContactId]['memberships']) && !empty($ownerMemIds
1622 ) && in_array($membershipValues['owner_membership_id'], $ownerMemIds[$relatedContactId])) {
eec8d770 1623 continue;
1624 }
f1e48e7f 1625
6a488035
TO
1626 //delete the membership record for related
1627 //contact before creating new membership record.
1628 CRM_Member_BAO_Membership::deleteRelatedMemberships($membershipId, $relatedContactId);
1629 }
1630
1631 // check whether we have some related memberships still available
1632 $query = "
1633SELECT count(*)
1634 FROM civicrm_membership
1635 LEFT JOIN civicrm_membership_status ON (civicrm_membership_status.id = civicrm_membership.status_id)
1636 WHERE membership_type_id = {$membershipValues['membership_type_id']} AND owner_membership_id = {$membershipValues['owner_membership_id']}
1637 AND is_current_member = 1";
1638 $result = CRM_Core_DAO::singleValueQuery($query);
1639 if ($result < CRM_Utils_Array::value('max_related', $membershipValues, PHP_INT_MAX)) {
43291913 1640 CRM_Member_BAO_Membership::create($membershipValues, CRM_Core_DAO::$_nullArray);
6a488035
TO
1641 }
1642 }
1643 }
1644 elseif ($action & CRM_Core_Action::UPDATE) {
1645 // if action is update and updated relationship do
1646 // not match with the existing
1647 // membership=>relationship then we need to
1648 // delete the membership record created for
1649 // previous relationship.
690c56c3 1650 // CRM-16087 we need to pass ownerMembershipId to deleteRelatedMemberships function
1651 if (empty($params['relationship_ids']) && !empty($params['id'])) {
1652 $relIds = array($params['id']);
1653 }
1654 else {
1655 $relIds = CRM_Utils_Array::value('relationship_ids', $params);
1656 }
1657 if (self::isDeleteRelatedMembership($relTypeIds, $contactId, $mainRelatedContactId, $relTypeId,
4d88abcc 1658 $relIds) && !empty($membershipValues['owner_membership_id']
1659 ) && !empty($values[$mainRelatedContactId]['memberships'][$membershipValues['owner_membership_id']])) {
690c56c3 1660 CRM_Member_BAO_Membership::deleteRelatedMemberships($membershipValues['owner_membership_id'], $membershipValues['membership_contact_id']);
6a488035
TO
1661 }
1662 }
1663 }
1664 }
1665 }
1666
1667 /**
98a06ae0 1668 * Helper function to check whether to delete the membership or not.
6616f7c1
EM
1669 *
1670 * Function takes a list of related membership types and if it is not also passed a
1671 * relationship ID of that types evaluates whether the membership should be deleted.
1672 *
1673 * @param array $membershipTypeRelationshipTypeIDs
1674 * Relation type IDs related to the given membership type.
1675 * @param int $contactId
1676 * @param int $mainRelatedContactId
1677 * @param int $relTypeId
1678 * @param array $relIds
1679 *
1680 * @return bool
6a488035 1681 */
6616f7c1
EM
1682 public static function isDeleteRelatedMembership($membershipTypeRelationshipTypeIDs, $contactId, $mainRelatedContactId, $relTypeId, $relIds) {
1683 if (empty($membershipTypeRelationshipTypeIDs) || in_array($relTypeId, $membershipTypeRelationshipTypeIDs)) {
eec8d770 1684 return FALSE;
6a488035
TO
1685 }
1686
1687 if (empty($relIds)) {
1688 return FALSE;
1689 }
1690
7f3647bc
TO
1691 $relParamas = array(
1692 1 => array($contactId, 'Integer'),
6a488035
TO
1693 2 => array($mainRelatedContactId, 'Integer'),
1694 );
1695
1696 if ($contactId == $mainRelatedContactId) {
6616f7c1
EM
1697 $recordsFound = (int) CRM_Core_DAO::singleValueQuery("SELECT COUNT(*) FROM civicrm_relationship WHERE relationship_type_id IN ( " . implode(',', $membershipTypeRelationshipTypeIDs) . " ) AND
1698contact_id_a IN ( %1 ) OR contact_id_b IN ( %1 ) AND id IN (" . implode(',', $relIds) . ")", $relParamas);
6a488035
TO
1699 if ($recordsFound) {
1700 return FALSE;
1701 }
1702 return TRUE;
1703 }
1704
6616f7c1 1705 $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
1706
1707 if ($recordsFound) {
1708 return FALSE;
1709 }
1710
1711 return TRUE;
1712 }
1713
1714 /**
fe482240 1715 * Get Current Employer for Contact.
6a488035 1716 *
77c5b619
TO
1717 * @param $contactIds
1718 * Contact Ids.
6a488035 1719 *
a6c01b45
CW
1720 * @return array
1721 * array of the current employer
6a488035 1722 */
00be9182 1723 public static function getCurrentEmployer($contactIds) {
6a488035
TO
1724 $contacts = implode(',', $contactIds);
1725
1726 $query = "
1727SELECT organization_name, id, employer_id
1728FROM civicrm_contact
1729WHERE id IN ( {$contacts} )
1730";
1731
1732 $dao = CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
1733 $currentEmployer = array();
1734 while ($dao->fetch()) {
1735 $currentEmployer[$dao->id]['org_id'] = $dao->employer_id;
1736 $currentEmployer[$dao->id]['org_name'] = $dao->organization_name;
1737 }
1738
1739 return $currentEmployer;
1740 }
1741
1742 /**
100fef9d 1743 * Return list of permissioned employer for a given contact.
6a488035 1744 *
5a4f6742
CW
1745 * @param int $contactID
1746 * contact id whose employers.
16b10e64 1747 * are to be found.
5a4f6742
CW
1748 * @param string $name
1749 * employers sort name.
6a488035 1750 *
a6c01b45
CW
1751 * @return array
1752 * array of employers.
6a488035 1753 */
00be9182 1754 public static function getPermissionedEmployer($contactID, $name = NULL) {
6a488035
TO
1755 //get the relationship id
1756 $relTypeId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_RelationshipType',
1757 'Employee of', 'id', 'name_a_b'
1758 );
1759
51e89def
DS
1760 return self::getPermissionedContacts($contactID, $relTypeId, $name);
1761 }
1762
1763
7f3647bc 1764 /**
fe482240 1765 * Function to return list of permissioned contacts for a given contact and relationship type.
7f3647bc 1766 *
5a4f6742
CW
1767 * @param int $contactID
1768 * contact id whose permissioned contacts are to be found.
1769 * @param string $relTypeId
1770 * one or more relationship type id's.
1771 * @param string $name
7f3647bc 1772 *
a6c01b45 1773 * @return array
16b10e64 1774 * Array of contacts
7f3647bc 1775 */
00be9182 1776 public static function getPermissionedContacts($contactID, $relTypeId, $name = NULL) {
51e89def
DS
1777 $contacts = array();
1778
6a488035
TO
1779 if ($relTypeId) {
1780 $query = "
1781SELECT cc.id as id, cc.sort_name as name
1782FROM civicrm_relationship cr, civicrm_contact cc
49f8272d 1783WHERE
51e89def 1784cr.contact_id_a = %1 AND
f9f0eff9 1785cr.relationship_type_id IN (%2) AND
51e89def 1786cr.is_permission_a_b = 1 AND
6a488035
TO
1787IF(cr.end_date IS NULL, 1, (DATEDIFF( CURDATE( ), cr.end_date ) <= 0)) AND
1788cr.is_active = 1 AND
7ecf6275 1789cc.id = cr.contact_id_b AND
4755bcde 1790cc.is_deleted = 0";
51e89def
DS
1791
1792 if (!empty($name)) {
7f3647bc 1793 $name = CRM_Utils_Type::escape($name, 'String');
51e89def 1794 $query .= "
49f8272d 1795AND cc.sort_name LIKE '%$name%'";
51e89def 1796 }
6a488035 1797
f9f0eff9 1798 $args = array(1 => array($contactID, 'Integer'), 2 => array($relTypeId, 'String'));
7f3647bc 1799 $dao = CRM_Core_DAO::executeQuery($query, $args);
6a488035
TO
1800
1801 while ($dao->fetch()) {
51e89def 1802 $contacts[$dao->id] = array(
6a488035
TO
1803 'name' => $dao->name,
1804 'value' => $dao->id,
1805 );
1806 }
1807 }
51e89def 1808 return $contacts;
6a488035
TO
1809 }
1810
6a488035 1811 /**
fe482240 1812 * Merge relationships from otherContact to mainContact.
bfa4da96 1813 *
6a488035
TO
1814 * Called during contact merge operation
1815 *
77c5b619
TO
1816 * @param int $mainId
1817 * Contact id of main contact record.
1818 * @param int $otherId
1819 * Contact id of record which is going to merge.
1820 * @param array $sqls
1821 * (reference) array of sql statements to append to.
6a488035
TO
1822 *
1823 * @see CRM_Dedupe_Merger::cpTables()
6a488035 1824 */
00be9182 1825 public static function mergeRelationships($mainId, $otherId, &$sqls) {
6a488035
TO
1826 // Delete circular relationships
1827 $sqls[] = "DELETE FROM civicrm_relationship
1828 WHERE (contact_id_a = $mainId AND contact_id_b = $otherId)
1829 OR (contact_id_b = $mainId AND contact_id_a = $otherId)";
1830
1831 // Delete relationship from other contact if main contact already has that relationship
1832 $sqls[] = "DELETE r2
1833 FROM civicrm_relationship r1, civicrm_relationship r2
1834 WHERE r1.relationship_type_id = r2.relationship_type_id
1835 AND r1.id <> r2.id
1836 AND (
1837 r1.contact_id_a = $mainId AND r2.contact_id_a = $otherId AND r1.contact_id_b = r2.contact_id_b
1838 OR r1.contact_id_b = $mainId AND r2.contact_id_b = $otherId AND r1.contact_id_a = r2.contact_id_a
1839 OR (
1840 (r1.contact_id_a = $mainId AND r2.contact_id_b = $otherId AND r1.contact_id_b = r2.contact_id_a
1841 OR r1.contact_id_b = $mainId AND r2.contact_id_a = $otherId AND r1.contact_id_a = r2.contact_id_b)
1842 AND r1.relationship_type_id IN (SELECT id FROM civicrm_relationship_type WHERE name_b_a = name_a_b)
1843 )
1844 )";
1845
1846 // Move relationships
1847 $sqls[] = "UPDATE IGNORE civicrm_relationship SET contact_id_a = $mainId WHERE contact_id_a = $otherId";
1848 $sqls[] = "UPDATE IGNORE civicrm_relationship SET contact_id_b = $mainId WHERE contact_id_b = $otherId";
1849
1850 // Move current employer id (name will get updated later)
1851 $sqls[] = "UPDATE civicrm_contact SET employer_id = $mainId WHERE employer_id = $otherId";
1852 }
1853
1854 /**
1855 * Set 'is_valid' field to false for all relationships whose end date is in the past, ie. are expired.
1856 *
72b3a70c
CW
1857 * @return bool
1858 * True on success, false if error is encountered.
6a488035 1859 */
00be9182 1860 public static function disableExpiredRelationships() {
6a488035
TO
1861 $query = "SELECT id FROM civicrm_relationship WHERE is_active = 1 AND end_date < CURDATE()";
1862
1863 $dao = CRM_Core_DAO::executeQuery($query);
1864 while ($dao->fetch()) {
1865 $result = CRM_Contact_BAO_Relationship::setIsActive($dao->id, FALSE);
1866 // Result will be NULL if error occurred. We abort early if error detected.
1867 if ($result == NULL) {
1868 return FALSE;
1869 }
1870 }
1871 return TRUE;
1872 }
c9c41397 1873
1874 /**
fe482240 1875 * Function filters the query by possible relationships for the membership type.
98a06ae0 1876 *
c9c41397 1877 * It is intended to be called when constructing queries for the api (reciprocal & non-reciprocal)
1878 * and to add clauses to limit the return to those relationships which COULD inherit a membership type
1879 * (as opposed to those who inherit a particular membership
1880 *
77c5b619
TO
1881 * @param array $params
1882 * Api input array.
77b97be7
EM
1883 * @param null $direction
1884 *
c301f76e 1885 * @return array|void
c9c41397 1886 */
00be9182 1887 public static function membershipTypeToRelationshipTypes(&$params, $direction = NULL) {
7f3647bc 1888 $membershipType = civicrm_api3('membership_type', 'getsingle', array(
353ffa53
TO
1889 'id' => $params['membership_type_id'],
1890 'return' => 'relationship_type_id, relationship_direction',
1891 ));
c9c41397 1892 $relationshipTypes = $membershipType['relationship_type_id'];
22e263ad 1893 if (empty($relationshipTypes)) {
c301f76e 1894 return NULL;
f201289d 1895 }
c9c41397 1896 // if we don't have any contact data we can only filter on type
22e263ad 1897 if (empty($params['contact_id']) && empty($params['contact_id_a']) && empty($params['contact_id_a'])) {
c9c41397 1898 $params['relationship_type_id'] = array('IN' => $relationshipTypes);
c301f76e 1899 return NULL;
c9c41397 1900 }
1901 else {
1a9b2ee8 1902 $relationshipDirections = (array) $membershipType['relationship_direction'];
c9c41397 1903 // if we have contact_id_a OR contact_id_b we can make a call here
1904 // if we have contact??
1905 foreach ($relationshipDirections as $index => $mtdirection) {
7f3647bc 1906 if (isset($params['contact_id_a']) && $mtdirection == 'a_b' || $direction == 'a_b') {
c9c41397 1907 $types[] = $relationshipTypes[$index];
1908 }
7f3647bc 1909 if (isset($params['contact_id_b']) && $mtdirection == 'b_a' || $direction == 'b_a') {
c9c41397 1910 $types[] = $relationshipTypes[$index];
1911 }
1912 }
22e263ad 1913 if (!empty($types)) {
c9c41397 1914 $params['relationship_type_id'] = array('IN' => $types);
1915 }
7f3647bc 1916 elseif (!empty($clauses)) {
c9c41397 1917 return explode(' OR ', $clauses);
1918 }
92e4c2a5 1919 else {
c9c41397 1920 // effectively setting it to return no results
1921 $params['relationship_type_id'] = 0;
1922 }
1923 }
1924 }
40458f6c 1925
1926
1927 /**
98a06ae0 1928 * Wrapper for contact relationship selector.
40458f6c 1929 *
77c5b619
TO
1930 * @param array $params
1931 * Associated array for params record id.
40458f6c 1932 *
a6c01b45
CW
1933 * @return array
1934 * associated array of contact relationships
40458f6c 1935 */
1936 public static function getContactRelationshipSelector(&$params) {
1937 // format the params
7f3647bc
TO
1938 $params['offset'] = ($params['page'] - 1) * $params['rp'];
1939 $params['sort'] = CRM_Utils_Array::value('sortBy', $params);
40458f6c 1940
1941 if ($params['context'] == 'past') {
1942 $relationshipStatus = CRM_Contact_BAO_Relationship::INACTIVE;
1943 }
62e6afce 1944 elseif ($params['context'] == 'all') {
6c292184
TM
1945 $relationshipStatus = CRM_Contact_BAO_Relationship::ALL;
1946 }
40458f6c 1947 else {
1948 $relationshipStatus = CRM_Contact_BAO_Relationship::CURRENT;
1949 }
1950
1951 // check logged in user for permission
1952 $page = new CRM_Core_Page();
1953 CRM_Contact_Page_View::checkUserPermission($page, $params['contact_id']);
1954 $permissions = array($page->_permission);
1955 if ($page->_permission == CRM_Core_Permission::EDIT) {
1956 $permissions[] = CRM_Core_Permission::DELETE;
1957 }
1958 $mask = CRM_Core_Action::mask($permissions);
1959
d0592c3d 1960 if ($params['context'] != 'user') {
1961 $links = CRM_Contact_Page_View_Relationship::links();
1962 $permissionedContacts = FALSE;
1963 }
1964 else {
1965 $links = CRM_Contact_Page_View_UserDashBoard::links();
1966 $permissionedContacts = TRUE;
1967 $mask = NULL;
1968 }
40458f6c 1969 // get contact relationships
1970 $relationships = CRM_Contact_BAO_Relationship::getRelationship($params['contact_id'],
1971 $relationshipStatus,
1972 $params['rp'], 0, 0,
d0592c3d 1973 $links, $mask,
1974 $permissionedContacts,
40458f6c 1975 $params
1976 );
1977
1978 $contactRelationships = array();
d0592c3d 1979 $params['total'] = 0;
40458f6c 1980 if (!empty($relationships)) {
2067937e
CW
1981 // FIXME: we cannot directly determine total permissioned relationship, hence re-fire query
1982 $params['total'] = $permissionedRelationships = CRM_Contact_BAO_Relationship::getRelationship($params['contact_id'],
1983 $relationshipStatus,
1984 0, 1, 0,
1985 NULL, NULL,
1986 $permissionedContacts
1987 );
40458f6c 1988
1989 // format params
1990 foreach ($relationships as $relationshipId => $values) {
67e4fb51 1991 //Add image icon for related contacts: CRM-14919
c6c691a5 1992 $icon = CRM_Contact_BAO_Contact_Utils::getImage($values['contact_type'],
67e4fb51
N
1993 FALSE,
1994 $values['cid']
1995 );
92fcb95f 1996 $contactRelationships[$relationshipId]['name'] = $icon . ' ' . CRM_Utils_System::href(
7f3647bc
TO
1997 $values['name'],
1998 'civicrm/contact/view',
1999 "reset=1&cid={$values['cid']}");
40458f6c 2000
2001 $contactRelationships[$relationshipId]['relation'] = CRM_Utils_System::href(
2002 $values['relation'],
2003 'civicrm/contact/view/rel',
f1cb718d 2004 "action=view&reset=1&cid={$values['cid']}&id={$values['id']}&rtype={$values['rtype']}");
40458f6c 2005
d0592c3d 2006 if ($params['context'] == 'current') {
40458f6c 2007 if (($params['contact_id'] == $values['contact_id_a'] AND $values['is_permission_a_b'] == 1) OR
2008 ($params['contact_id'] == $values['contact_id_b'] AND $values['is_permission_b_a'] == 1)
2009 ) {
2010 $contactRelationships[$relationshipId]['name'] .= '<span id="permission-a-b" class="crm-marker permission-relationship"> *</span>';
2011 }
2012
2013 if (($values['cid'] == $values['contact_id_a'] AND $values['is_permission_a_b'] == 1) OR
2014 ($values['cid'] == $values['contact_id_b'] AND $values['is_permission_b_a'] == 1)
2015 ) {
2016 $contactRelationships[$relationshipId]['relation'] .= '<span id="permission-b-a" class="crm-marker permission-relationship"> *</span>';
2017 }
2018 }
2019
2020 if (!empty($values['description'])) {
2021 $contactRelationships[$relationshipId]['relation'] .= "<p class='description'>{$values['description']}</p>";
2022 }
2023
2024 $contactRelationships[$relationshipId]['start_date'] = CRM_Utils_Date::customFormat($values['start_date']);
2025 $contactRelationships[$relationshipId]['end_date'] = CRM_Utils_Date::customFormat($values['end_date']);
2026 $contactRelationships[$relationshipId]['city'] = $values['city'];
2027 $contactRelationships[$relationshipId]['state'] = $values['state'];
2028 $contactRelationships[$relationshipId]['email'] = $values['email'];
2029 $contactRelationships[$relationshipId]['phone'] = $values['phone'];
2030 $contactRelationships[$relationshipId]['links'] = $values['action'];
e1cad725 2031 $contactRelationships[$relationshipId]['id'] = $values['id'];
f1321272 2032 $contactRelationships[$relationshipId]['is_active'] = $values['is_active'];
40458f6c 2033 }
2034 }
2035 return $contactRelationships;
2036 }
2037
6a488035 2038}