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