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