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