Merge pull request #15970 from eileenmcnaughton/ex_easy
[civicrm-core.git] / CRM / Case / BAO / Case.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18/**
cde2037d 19 * This class contains the functions for Case Management.
6a488035
TO
20 */
21class CRM_Case_BAO_Case extends CRM_Case_DAO_Case {
22
23 /**
cde2037d 24 * Static field for all the case information that we can potentially export.
6a488035
TO
25 *
26 * @var array
6a488035 27 */
f157740d 28 public static $_exportableFields = NULL;
e96f025a 29
4c6ce474 30 /**
cde2037d 31 * Class constructor.
4c6ce474 32 */
00be9182 33 public function __construct() {
6a488035
TO
34 parent::__construct();
35 }
36
37 /**
077dbf5e
CW
38 * Is CiviCase enabled?
39 *
40 * @return bool
41 */
00be9182 42 public static function enabled() {
077dbf5e
CW
43 $config = CRM_Core_Config::singleton();
44 return in_array('CiviCase', $config->enableComponents);
45 }
46
6a488035 47 /**
cde2037d 48 * Create a case object.
6a488035 49 *
cde2037d 50 * The function extracts all the params it needs to initialize the create a
6a488035
TO
51 * case object. the params array could contain additional unused name/value
52 * pairs
53 *
64bd5a0e
TO
54 * @param array $params
55 * (reference ) an assoc array of name/value pairs.
77b97be7 56 *
16b10e64 57 * @return CRM_Case_BAO_Case
6a488035 58 */
00be9182 59 public static function add(&$params) {
6a488035
TO
60 $caseDAO = new CRM_Case_DAO_Case();
61 $caseDAO->copyValues($params);
da4d8910 62 $result = $caseDAO->save();
5d4fcf54
TO
63 // Get other case values (required by XML processor), this adds to $result array
64 $caseDAO->find(TRUE);
da4d8910 65 return $result;
6a488035
TO
66 }
67
6a488035 68 /**
cde2037d 69 * Takes an associative array and creates a case object.
6a488035 70 *
64bd5a0e 71 * @param array $params
cde2037d 72 * (reference) an assoc array of name/value pairs.
77b97be7 73 *
16b10e64 74 * @return CRM_Case_BAO_Case
6a488035 75 */
00be9182 76 public static function &create(&$params) {
ea6a17a9
TO
77 // CRM-20958 - These fields are managed by MySQL triggers. Watch out for clients resaving stale timestamps.
78 unset($params['created_date']);
79 unset($params['modified_date']);
3717c347
JP
80 $caseStatus = CRM_Case_PseudoConstant::caseStatus('name');
81 // for resolved case the end date should set to now
82 if (!empty($params['status_id']) && $params['status_id'] == array_search('Closed', $caseStatus)) {
83 $params['end_date'] = date("Ymd");
84 }
ea6a17a9 85
6a488035
TO
86 $transaction = new CRM_Core_Transaction();
87
a7488080 88 if (!empty($params['id'])) {
6a488035
TO
89 CRM_Utils_Hook::pre('edit', 'Case', $params['id'], $params);
90 }
91 else {
92 CRM_Utils_Hook::pre('create', 'Case', NULL, $params);
93 }
94
95 $case = self::add($params);
96
a7488080 97 if (!empty($params['custom']) &&
6a488035
TO
98 is_array($params['custom'])
99 ) {
100 CRM_Core_BAO_CustomValueTable::store($params['custom'], 'civicrm_case', $case->id);
101 }
102
103 if (is_a($case, 'CRM_Core_Error')) {
104 $transaction->rollback();
105 return $case;
106 }
107
a7488080 108 if (!empty($params['id'])) {
6a488035
TO
109 CRM_Utils_Hook::post('edit', 'Case', $case->id, $case);
110 }
111 else {
112 CRM_Utils_Hook::post('create', 'Case', $case->id, $case);
113 }
114 $transaction->commit();
115
116 //we are not creating log for case
117 //since case log can be tracked using log for activity.
118 return $case;
119 }
120
6a488035 121 /**
100fef9d 122 * Process case activity add/delete
6a488035
TO
123 * takes an associative array and
124 *
64bd5a0e
TO
125 * @param array $params
126 * (reference ) an assoc array of name/value pairs.
6a488035 127 *
6a488035 128 */
00be9182 129 public static function processCaseActivity(&$params) {
6a488035
TO
130 $caseActivityDAO = new CRM_Case_DAO_CaseActivity();
131 $caseActivityDAO->activity_id = $params['activity_id'];
132 $caseActivityDAO->case_id = $params['case_id'];
133
134 $caseActivityDAO->find(TRUE);
135 $caseActivityDAO->save();
136 }
137
138 /**
cde2037d 139 * Get the case subject for Activity.
6a488035 140 *
64bd5a0e
TO
141 * @param int $activityId
142 * Activity id.
6a488035 143 *
2b37475d 144 * @return string|null
6a488035 145 */
00be9182 146 public static function getCaseSubject($activityId) {
6a488035
TO
147 $caseActivity = new CRM_Case_DAO_CaseActivity();
148 $caseActivity->activity_id = $activityId;
149 if ($caseActivity->find(TRUE)) {
150 return CRM_Core_DAO::getFieldValue('CRM_Case_BAO_Case', $caseActivity->case_id, 'subject');
151 }
152 return NULL;
153 }
154
155 /**
100fef9d 156 * Get the case type.
6a488035
TO
157 *
158 * @param int $caseId
77b97be7
EM
159 * @param string $colName
160 *
a6c01b45
CW
161 * @return string
162 * case type
6a488035 163 */
00be9182 164 public static function getCaseType($caseId, $colName = 'title') {
8ffdec17
ARW
165 $query = "
166SELECT civicrm_case_type.{$colName} FROM civicrm_case
167LEFT JOIN civicrm_case_type ON
168 civicrm_case.case_type_id = civicrm_case_type.id
169WHERE civicrm_case.id = %1";
6a488035 170
fc9f05e0 171 $queryParams = [1 => [$caseId, 'Integer']];
6a488035 172
8ffdec17 173 return CRM_Core_DAO::singleValueQuery($query, $queryParams);
6a488035
TO
174 }
175
176 /**
d2e5d2ce 177 * Delete the record that are associated with this case.
6a488035
TO
178 * record are deleted from case
179 *
64bd5a0e
TO
180 * @param int $caseId
181 * Id of the case to delete.
6a488035 182 *
77b97be7
EM
183 * @param bool $moveToTrash
184 *
a6c01b45
CW
185 * @return bool
186 * is successful
6a488035 187 */
00be9182 188 public static function deleteCase($caseId, $moveToTrash = FALSE) {
6a488035
TO
189 CRM_Utils_Hook::pre('delete', 'Case', $caseId, CRM_Core_DAO::$_nullArray);
190
191 //delete activities
192 $activities = self::getCaseActivityDates($caseId);
193 if ($activities) {
194 foreach ($activities as $value) {
195 CRM_Activity_BAO_Activity::deleteActivity($value, $moveToTrash);
196 }
197 }
198
199 if (!$moveToTrash) {
200 $transaction = new CRM_Core_Transaction();
201 }
202 $case = new CRM_Case_DAO_Case();
203 $case->id = $caseId;
204 if (!$moveToTrash) {
205 $result = $case->delete();
206 $transaction->commit();
207 }
208 else {
209 $result = $case->is_deleted = 1;
210 $case->save();
211 }
212
213 if ($result) {
214 // CRM-7364, disable relationships
215 self::enableDisableCaseRelationships($caseId, FALSE);
216
217 CRM_Utils_Hook::post('delete', 'Case', $caseId, $case);
218
219 // remove case from recent items.
fc9f05e0 220 $caseRecent = [
6a488035
TO
221 'id' => $caseId,
222 'type' => 'Case',
fc9f05e0 223 ];
6a488035
TO
224 CRM_Utils_Recent::del($caseRecent);
225 return TRUE;
226 }
227
228 return FALSE;
229 }
230
231 /**
cde2037d 232 * Enable disable case related relationships.
6a488035 233 *
64bd5a0e
TO
234 * @param int $caseId
235 * Case id.
236 * @param bool $enable
237 * Action.
6a488035 238 */
00be9182 239 public static function enableDisableCaseRelationships($caseId, $enable) {
6a488035
TO
240 $contactIds = self::retrieveContactIdsByCaseId($caseId);
241 if (!empty($contactIds)) {
242 foreach ($contactIds as $cid) {
243 $roles = self::getCaseRoles($cid, $caseId);
244 if (!empty($roles)) {
245 $relationshipIds = implode(',', array_keys($roles));
e96f025a 246 $enable = (int) $enable;
247 $query = "UPDATE civicrm_relationship SET is_active = {$enable}
6a488035
TO
248 WHERE id IN ( {$relationshipIds} )";
249 CRM_Core_DAO::executeQuery($query);
250 }
251 }
252 }
253 }
254
6a488035 255 /**
fe482240 256 * Retrieve contact_id by case_id.
6a488035 257 *
64bd5a0e
TO
258 * @param int $caseId
259 * ID of the case.
77b97be7 260 *
100fef9d 261 * @param int $contactID
ea2aa8ff 262 * @param int $startArrayAt This is to support legacy calls to Case.Get API which may rely on the first array index being set to 1
6a488035
TO
263 *
264 * @return array
6a488035 265 */
ea2aa8ff 266 public static function retrieveContactIdsByCaseId($caseId, $contactID = NULL, $startArrayAt = 0) {
6a488035
TO
267 $caseContact = new CRM_Case_DAO_CaseContact();
268 $caseContact->case_id = $caseId;
269 $caseContact->find();
fc9f05e0 270 $contactArray = [];
ea2aa8ff 271 $count = $startArrayAt;
6a488035
TO
272 while ($caseContact->fetch()) {
273 if ($contactID != $caseContact->contact_id) {
274 $contactArray[$count] = $caseContact->contact_id;
275 $count++;
276 }
277 }
278
279 return $contactArray;
280 }
281
282 /**
cde2037d 283 * Look up a case using an activity ID.
6a488035 284 *
100fef9d 285 * @param int $activityId
77b97be7 286 *
6a488035
TO
287 * @return int, case ID
288 */
00be9182 289 public static function getCaseIdByActivityId($activityId) {
6a488035
TO
290 $originalId = CRM_Core_DAO::singleValueQuery(
291 'SELECT original_id FROM civicrm_activity WHERE id = %1',
fc9f05e0 292 ['1' => [$activityId, 'Integer']]
6a488035
TO
293 );
294 $caseId = CRM_Core_DAO::singleValueQuery(
295 'SELECT case_id FROM civicrm_case_activity WHERE activity_id in (%1,%2)',
fc9f05e0 296 [
297 '1' => [$activityId, 'Integer'],
298 '2' => [$originalId ? $originalId : $activityId, 'Integer'],
299 ]
6a488035
TO
300 );
301 return $caseId;
302 }
303
304 /**
d2e5d2ce 305 * Retrieve contact names by caseId.
6a488035 306 *
64bd5a0e
TO
307 * @param int $caseId
308 * ID of the case.
6a488035
TO
309 *
310 * @return array
6a488035 311 */
00be9182 312 public static function getContactNames($caseId) {
fc9f05e0 313 $contactNames = [];
6a488035
TO
314 if (!$caseId) {
315 return $contactNames;
316 }
317
318 $query = "
319 SELECT contact_a.sort_name name,
320 contact_a.display_name as display_name,
321 contact_a.id cid,
322 contact_a.birth_date as birth_date,
323 ce.email as email,
324 cp.phone as phone
325 FROM civicrm_contact contact_a
326 LEFT JOIN civicrm_case_contact ON civicrm_case_contact.contact_id = contact_a.id
327 LEFT JOIN civicrm_email ce ON ( ce.contact_id = contact_a.id AND ce.is_primary = 1)
328 LEFT JOIN civicrm_phone cp ON ( cp.contact_id = contact_a.id AND cp.is_primary = 1)
6b576a7c 329 WHERE contact_a.is_deleted = 0 AND civicrm_case_contact.case_id = %1
78762324 330 ORDER BY civicrm_case_contact.id";
6a488035
TO
331
332 $dao = CRM_Core_DAO::executeQuery($query,
fc9f05e0 333 [1 => [$caseId, 'Integer']]
6a488035
TO
334 );
335 while ($dao->fetch()) {
336 $contactNames[$dao->cid]['contact_id'] = $dao->cid;
337 $contactNames[$dao->cid]['sort_name'] = $dao->name;
338 $contactNames[$dao->cid]['display_name'] = $dao->display_name;
339 $contactNames[$dao->cid]['email'] = $dao->email;
340 $contactNames[$dao->cid]['phone'] = $dao->phone;
341 $contactNames[$dao->cid]['birth_date'] = $dao->birth_date;
342 $contactNames[$dao->cid]['role'] = ts('Client');
343 }
344
345 return $contactNames;
346 }
347
348 /**
cde2037d 349 * Retrieve case_id by contact_id.
6a488035 350 *
100fef9d 351 * @param int $contactID
64bd5a0e
TO
352 * @param bool $includeDeleted
353 * Include the deleted cases in result.
77b97be7
EM
354 * @param null $caseType
355 *
6a488035 356 * @return array
6a488035 357 */
00be9182 358 public static function retrieveCaseIdsByContactId($contactID, $includeDeleted = FALSE, $caseType = NULL) {
6a488035
TO
359 $query = "
360SELECT ca.id as id
361FROM civicrm_case_contact cc
362INNER JOIN civicrm_case ca ON cc.case_id = ca.id
6a488035 363";
1d8da9d9
DJ
364 if (isset($caseType)) {
365 $query .=
76304e2f 366 "INNER JOIN civicrm_case_type ON civicrm_case_type.id = ca.case_type_id
8ffdec17 367WHERE cc.contact_id = %1 AND civicrm_case_type.name = '{$caseType}'";
1d8da9d9 368 }
a2da6067
DJ
369 if (!isset($caseType)) {
370 $query .= "WHERE cc.contact_id = %1";
371 }
6a488035
TO
372 if (!$includeDeleted) {
373 $query .= " AND ca.is_deleted = 0";
374 }
375
fc9f05e0 376 $params = [1 => [$contactID, 'Integer']];
6a488035
TO
377 $dao = CRM_Core_DAO::executeQuery($query, $params);
378
fc9f05e0 379 $caseArray = [];
6a488035
TO
380 while ($dao->fetch()) {
381 $caseArray[] = $dao->id;
382 }
383
6a488035
TO
384 return $caseArray;
385 }
386
4c6ce474
EM
387 /**
388 * @param string $type
100fef9d 389 * @param int $userID
d6eb0c11 390 * @param string $condition
4c6ce474
EM
391 *
392 * @return string
393 */
5f1c8c57 394 public static function getCaseActivityCountQuery($type = 'upcoming', $userID, $condition = NULL) {
395 return sprintf(" SELECT COUNT(*) FROM (%s) temp ", self::getCaseActivityQuery($type, $userID, $condition));
396 }
6a488035 397
5f1c8c57 398 /**
399 * @param string $type
400 * @param int $userID
401 * @param string $condition
402 * @param string $limit
f157740d 403 * @param string $order
5f1c8c57 404 *
405 * @return string
406 */
407 public static function getCaseActivityQuery($type = 'upcoming', $userID, $condition = NULL, $limit = NULL, $order = NULL) {
fc9f05e0 408 $selectClauses = [
5f1c8c57 409 'civicrm_case.id as case_id',
410 'civicrm_case.subject as case_subject',
411 'civicrm_contact.id as contact_id',
412 'civicrm_contact.sort_name as sort_name',
413 'civicrm_phone.phone as phone',
414 'civicrm_contact.contact_type as contact_type',
415 'civicrm_contact.contact_sub_type as contact_sub_type',
8cf4fbdb 416 't_act.activity_type_id as activity_type_id',
5f1c8c57 417 'civicrm_case.case_type_id as case_type_id',
57d38398 418 'civicrm_case.status_id as case_status_id',
8cf4fbdb 419 't_act.status_id as status_id',
5f1c8c57 420 'civicrm_case.start_date as case_start_date',
41cf58d3 421 "GROUP_CONCAT(DISTINCT IF(case_relationship.contact_id_b = $userID, case_relation_type.label_a_b, case_relation_type.label_b_a) SEPARATOR ', ') as case_role",
2c783cce
AH
422 't_act.activity_date_time as activity_date_time',
423 't_act.id as activity_id',
fc9f05e0 424 ];
6a488035 425
5f1c8c57 426 $query = CRM_Contact_BAO_Query::appendAnyValueToSelect($selectClauses, 'case_id');
427
9d22ae15
AH
428 $query .= <<<HERESQL
429 FROM civicrm_case
430 INNER JOIN civicrm_case_contact ON civicrm_case.id = civicrm_case_contact.case_id
431 INNER JOIN civicrm_contact ON civicrm_case_contact.contact_id = civicrm_contact.id
432HERESQL;
433
434 switch ($type) {
435 case 'upcoming':
436 case 'recent':
437 // civicrm_view_case_activity_upcoming and
438 // civicrm_view_case_activity_recent are views that show the next
439 // scheduled and most recent not-scheduled activity on each case,
440 // respectively.
441 $query .= <<<HERESQL
442 INNER JOIN civicrm_view_case_activity_$type t_act
443 ON t_act.case_id = civicrm_case.id
444HERESQL;
445 break;
446
447 case 'any':
448 $query .= <<<HERESQL
449 LEFT JOIN civicrm_case_activity ca4
450 ON civicrm_case.id = ca4.case_id
451 LEFT JOIN civicrm_activity t_act
452 ON t_act.id = ca4.activity_id
453 AND t_act.is_current_revision = 1
454HERESQL;
90319720 455 }
6a488035 456
9d22ae15
AH
457 $query .= <<<HERESQL
458 LEFT JOIN civicrm_phone
459 ON civicrm_phone.contact_id = civicrm_contact.id
460 AND civicrm_phone.is_primary = 1
461 LEFT JOIN civicrm_relationship case_relationship
41cf58d3
AF
462 ON ((case_relationship.contact_id_a = civicrm_case_contact.contact_id AND case_relationship.contact_id_b = {$userID})
463 OR (case_relationship.contact_id_b = civicrm_case_contact.contact_id AND case_relationship.contact_id_a = {$userID}))
9d22ae15
AH
464 AND case_relationship.is_active
465 AND case_relationship.case_id = civicrm_case.id
466 LEFT JOIN civicrm_relationship_type case_relation_type
467 ON case_relation_type.id = case_relationship.relationship_type_id
468 AND case_relation_type.id = case_relationship.relationship_type_id
469HERESQL;
6a488035
TO
470
471 if ($condition) {
472 // CRM-8749 backwards compatibility - callers of this function expect to start $condition with "AND"
5f1c8c57 473 $query .= " WHERE (1) AND $condition ";
6a488035 474 }
5f1c8c57 475 $query .= " GROUP BY case_id ";
6a488035 476
2c783cce 477 $query .= ($order) ?: ' ORDER BY activity_date_time ASC';
5f1c8c57 478
479 if ($limit) {
480 $query .= $limit;
90319720 481 }
6a488035
TO
482
483 return $query;
484 }
485
486 /**
cde2037d 487 * Retrieve cases related to particular contact or whole contact used in Dashboard and Tab.
6a488035 488 *
64bd5a0e 489 * @param bool $allCases
5f1c8c57 490 * @param array $params
77b97be7 491 * @param string $context
5f1c8c57 492 * @param bool $getCount
77b97be7 493 *
a6c01b45
CW
494 * @return array
495 * Array of Cases
6a488035 496 */
fc9f05e0 497 public static function getCases($allCases = TRUE, $params = [], $context = 'dashboard', $getCount = FALSE) {
6a488035 498 $condition = NULL;
fc9f05e0 499 $casesList = [];
6a488035 500
d83ee565 501 // validate access for own cases.
6a488035 502 if (!self::accessCiviCase()) {
5f1c8c57 503 return $getCount ? 0 : $casesList;
6a488035
TO
504 }
505
d83ee565
MWMC
506 // Return cached value instead of re-running query
507 if (isset(Civi::$statics[__CLASS__]['totalCount']) && $getCount) {
508 return Civi::$statics[__CLASS__]['totalCount'];
509 }
510
5f1c8c57 511 $type = CRM_Utils_Array::value('type', $params, 'upcoming');
512 $userID = CRM_Core_Session::singleton()->get('userID');
513
d83ee565 514 // validate access for all cases.
6a488035
TO
515 if ($allCases && !CRM_Core_Permission::check('access all cases and activities')) {
516 $allCases = FALSE;
517 }
518
fc9f05e0 519 $whereClauses = ['civicrm_case.is_deleted = 0 AND civicrm_contact.is_deleted <> 1'];
6a488035
TO
520
521 if (!$allCases) {
41cf58d3
AF
522 $whereClauses[] = "(case_relationship.contact_id_b = {$userID} OR case_relationship.contact_id_a = {$userID})";
523 $whereClauses[] = 'case_relationship.is_active';
6a488035 524 }
5f1c8c57 525 if (empty($params['status_id']) && ($type == 'upcoming' || $type == 'any')) {
41cf58d3 526 $whereClauses[] = "civicrm_case.status_id != " . CRM_Core_PseudoConstant::getKey('CRM_Case_BAO_Case', 'case_status_id', 'Closed');
6a488035
TO
527 }
528
fc9f05e0 529 foreach (['case_type_id', 'status_id'] as $column) {
5f1c8c57 530 if (!empty($params[$column])) {
531 $whereClauses[] = sprintf("civicrm_case.%s IN (%s)", $column, $params[$column]);
532 }
533 }
534 $condition = implode(' AND ', $whereClauses);
6a488035 535
d83ee565 536 Civi::$statics[__CLASS__]['totalCount'] = $totalCount = CRM_Core_DAO::singleValueQuery(self::getCaseActivityCountQuery($type, $userID, $condition));
5f1c8c57 537 if ($getCount) {
538 return $totalCount;
6a488035 539 }
5f1c8c57 540 $casesList['total'] = $totalCount;
541
542 $limit = '';
543 if (!empty($params['rp'])) {
544 $params['offset'] = ($params['page'] - 1) * $params['rp'];
545 $params['rowCount'] = $params['rp'];
546 if (!empty($params['rowCount']) && $params['rowCount'] > 0) {
547 $limit = " LIMIT {$params['offset']}, {$params['rowCount']} ";
548 }
6a488035 549 }
5f1c8c57 550
551 $order = NULL;
552 if (!empty($params['sortBy'])) {
553 if (strstr($params['sortBy'], 'date ')) {
2c783cce 554 $params['sortBy'] = str_replace('date', 'activity_date_time', $params['sortBy']);
5f1c8c57 555 }
556 $order = "ORDER BY " . $params['sortBy'];
90319720 557 }
6a488035 558
5f1c8c57 559 $query = self::getCaseActivityQuery($type, $userID, $condition, $limit, $order);
560 $result = CRM_Core_DAO::executeQuery($query);
561
6a488035
TO
562 // we're going to use the usual actions, so doesn't make sense to duplicate definitions
563 $actions = CRM_Case_Selector_Search::links();
564
6a488035 565 // check is the user has view/edit signer permission
fc9f05e0 566 $permissions = [CRM_Core_Permission::VIEW];
6a488035
TO
567 if (CRM_Core_Permission::check('access all cases and activities') ||
568 (!$allCases && CRM_Core_Permission::check('access my cases and activities'))
569 ) {
570 $permissions[] = CRM_Core_Permission::EDIT;
571 }
572 if (CRM_Core_Permission::check('delete in CiviCase')) {
573 $permissions[] = CRM_Core_Permission::DELETE;
574 }
575 $mask = CRM_Core_Action::mask($permissions);
576
2c783cce
AH
577 // Pseudoconstants to populate labels
578 $caseStatuses = CRM_Case_PseudoConstant::caseStatus('label', FALSE);
5f1c8c57 579 $caseTypes = CRM_Case_PseudoConstant::caseType('name');
57d38398 580 $caseTypeTitles = CRM_Case_PseudoConstant::caseType('title', FALSE);
2c783cce
AH
581 $activityTypeLabels = CRM_Activity_BAO_Activity::buildOptions('activity_type_id');
582
bf5990e7 583 foreach ($result->fetchAll() as $case) {
584 $key = $case['case_id'];
fc9f05e0 585 $casesList[$key] = [];
5f1c8c57 586 $casesList[$key]['DT_RowId'] = $case['case_id'];
fc9f05e0 587 $casesList[$key]['DT_RowAttr'] = ['data-entity' => 'case', 'data-id' => $case['case_id']];
5f1c8c57 588 $casesList[$key]['DT_RowClass'] = "crm-entity";
589
590 $casesList[$key]['activity_list'] = sprintf('<a title="%s" class="crm-expand-row" href="%s"></a>',
591 ts('Activities'),
fc9f05e0 592 CRM_Utils_System::url('civicrm/case/details', ['caseId' => $case['case_id'], 'cid' => $case['contact_id'], 'type' => $type])
5f1c8c57 593 );
594
595 $phone = empty($case['phone']) ? '' : '<br /><span class="description">' . $case['phone'] . '</span>';
596 $casesList[$key]['contact_id'] = sprintf('<a href="%s">%s</a>%s<br /><span class="description">%s: %d</span>',
fc9f05e0 597 CRM_Utils_System::url('civicrm/contact/view', ['cid' => $case['contact_id']]),
5f1c8c57 598 $case['sort_name'],
599 $phone,
600 ts('Case ID'),
601 $case['case_id']
602 );
603 $casesList[$key]['subject'] = $case['case_subject'];
57d38398 604 $casesList[$key]['case_status'] = CRM_Utils_Array::value($case['case_status_id'], $caseStatuses);
2c783cce 605 if ($case['case_status_id'] == CRM_Case_PseudoConstant::getKey('CRM_Case_BAO_Case', 'case_status_id', 'Urgent')) {
57d38398
AH
606 $casesList[$key]['case_status'] = sprintf('<strong>%s</strong>', strtoupper($casesList[$key]['case_status']));
607 }
608 $casesList[$key]['case_type'] = CRM_Utils_Array::value($case['case_type_id'], $caseTypeTitles);
5f1c8c57 609 $casesList[$key]['case_role'] = CRM_Utils_Array::value('case_role', $case, '---');
c00bd201 610 $casesList[$key]['manager'] = self::getCaseManagerContact($caseTypes[$case['case_type_id']], $case['case_id']);
5f1c8c57 611
2c783cce
AH
612 $casesList[$key]['date'] = CRM_Utils_Array::value($case['activity_type_id'], $activityTypeLabels);
613 if ($actId = CRM_Utils_Array::value('activity_id', $case)) {
5f1c8c57 614 if (self::checkPermission($actId, 'view', $case['activity_type_id'], $userID)) {
615 if ($type == 'recent') {
616 $casesList[$key]['date'] = sprintf('<a class="action-item crm-hover-button" href="%s" title="%s">%s</a>',
fc9f05e0 617 CRM_Utils_System::url('civicrm/case/activity/view', ['reset' => 1, 'cid' => $case['contact_id'], 'aid' => $case['activity_id']]),
5f1c8c57 618 ts('View activity'),
2c783cce 619 CRM_Utils_Array::value($case['activity_type_id'], $activityTypeLabels)
5f1c8c57 620 );
6a488035
TO
621 }
622 else {
2c783cce 623 $status = CRM_Utils_Date::overdue($case['activity_date_time']) ? 'status-overdue' : 'status-scheduled';
5f1c8c57 624 $casesList[$key]['date'] = sprintf('<a class="crm-popup %s" href="%s" title="%s">%s</a> &nbsp;&nbsp;',
fc9f05e0 625 $status,
626 CRM_Utils_System::url('civicrm/case/activity/view', ['reset' => 1, 'cid' => $case['contact_id'], 'aid' => $case['activity_id']]),
5f1c8c57 627 ts('View activity'),
2c783cce 628 CRM_Utils_Array::value($case['activity_type_id'], $activityTypeLabels)
5f1c8c57 629 );
6a488035
TO
630 }
631 }
cedb74cd 632 if (isset($case['activity_type_id']) && self::checkPermission($actId, 'edit', $case['activity_type_id'], $userID)) {
5f1c8c57 633 $casesList[$key]['date'] .= sprintf('<a class="action-item crm-hover-button" href="%s" title="%s"><i class="crm-i fa-pencil"></i></a>',
fc9f05e0 634 CRM_Utils_System::url('civicrm/case/activity', ['reset' => 1, 'cid' => $case['contact_id'], 'caseid' => $case['case_id'], 'action' => 'update', 'id' => $actId]),
5f1c8c57 635 ts('Edit activity')
636 );
637 }
6a488035 638 }
2c783cce 639 $casesList[$key]['date'] .= "<br/>" . CRM_Utils_Date::customFormat($case['activity_date_time']);
5f1c8c57 640 $casesList[$key]['links'] = CRM_Core_Action::formLink($actions['primaryActions'], $mask,
fc9f05e0 641 [
5f1c8c57 642 'id' => $case['case_id'],
643 'cid' => $case['contact_id'],
644 'cxt' => $context,
fc9f05e0 645 ],
5f1c8c57 646 ts('more'),
647 FALSE,
648 'case.actions.primary',
649 'Case',
650 $case['case_id']
651 );
6a488035
TO
652 }
653
654 return $casesList;
655 }
656
657 /**
100fef9d
CW
658 * Get the summary of cases counts by type and status.
659 *
660 * @param bool $allCases
fc9f05e0 661 *
100fef9d 662 * @return array
6a488035 663 */
5f1c8c57 664 public static function getCasesSummary($allCases = TRUE) {
fc9f05e0 665 $caseSummary = [];
6a488035
TO
666
667 //validate access for civicase.
668 if (!self::accessCiviCase()) {
669 return $caseSummary;
670 }
671
5f1c8c57 672 $userID = CRM_Core_Session::singleton()->get('userID');
673
6a488035
TO
674 //validate access for all cases.
675 if ($allCases && !CRM_Core_Permission::check('access all cases and activities')) {
676 $allCases = FALSE;
677 }
678
e96f025a 679 $caseTypes = CRM_Case_PseudoConstant::caseType();
6a488035 680 $caseStatuses = CRM_Case_PseudoConstant::caseStatus();
e96f025a 681 $caseTypes = array_flip($caseTypes);
6a488035
TO
682
683 // get statuses as headers for the table
684 $url = CRM_Utils_System::url('civicrm/case/search', "reset=1&force=1&all=1&status=");
685 foreach ($caseStatuses as $key => $name) {
686 $caseSummary['headers'][$key]['status'] = $name;
687 $caseSummary['headers'][$key]['url'] = $url . $key;
688 }
689
690 // build rows with actual data
fc9f05e0 691 $rows = [];
41cf58d3 692 $myGroupByClause = $mySelectClause = $myCaseFromClause = $myCaseWhereClauseA = $myCaseWhereClauseB = '';
6a488035
TO
693
694 if ($allCases) {
695 $userID = 'null';
696 $all = 1;
697 $case_owner = 1;
41cf58d3 698 $myGroupByClauseB = ' GROUP BY civicrm_case.id';
6a488035
TO
699 }
700 else {
e96f025a 701 $all = 0;
6a488035 702 $case_owner = 2;
41cf58d3
AF
703 $myCaseWhereClauseA = " AND case_relationship.contact_id_a = {$userID} AND case_relationship.is_active ";
704 $myGroupByClauseA = " GROUP BY CONCAT(civicrm_case.id,'-',case_relationship.contact_id_a)";
705 $myCaseWhereClauseB = " AND case_relationship.contact_id_b = {$userID} AND case_relationship.is_active ";
706 $myGroupByClauseB = " GROUP BY CONCAT(civicrm_case.id,'-',case_relationship.contact_id_b)";
6a488035 707 }
41cf58d3
AF
708 $myGroupByClauseB .= ", case_status.label, status_id, case_type_id, civicrm_case.id";
709 $myGroupByClauseA = $myGroupByClauseB;
d5bea1e3 710 // FIXME: This query could be a lot more efficient if it used COUNT() instead of returning all rows and then counting them with php
6a488035 711 $query = "
a1afe798 712SELECT civicrm_case.id, case_status.label AS case_status, status_id, civicrm_case_type.title AS case_type,
41cf58d3 713 case_type_id, case_relationship.contact_id_b as case_contact
6a488035 714 FROM civicrm_case
d5bea1e3 715 INNER JOIN civicrm_case_contact cc on cc.case_id = civicrm_case.id
8ffdec17 716 LEFT JOIN civicrm_case_type ON civicrm_case.case_type_id = civicrm_case_type.id
6a488035
TO
717 LEFT JOIN civicrm_option_group option_group_case_status ON ( option_group_case_status.name = 'case_status' )
718 LEFT JOIN civicrm_option_value case_status ON ( civicrm_case.status_id = case_status.value
719 AND option_group_case_status.id = case_status.option_group_id )
720 LEFT JOIN civicrm_relationship case_relationship ON ( case_relationship.case_id = civicrm_case.id
aee55e82 721 AND case_relationship.contact_id_b = {$userID} AND case_relationship.is_active )
d5bea1e3 722 WHERE is_deleted = 0 AND cc.contact_id IN (SELECT id FROM civicrm_contact WHERE is_deleted <> 1)
41cf58d3
AF
723{$myCaseWhereClauseB} {$myGroupByClauseB}
724UNION
a1afe798 725SELECT civicrm_case.id, case_status.label AS case_status, status_id, civicrm_case_type.title AS case_type,
41cf58d3
AF
726 case_type_id, case_relationship.contact_id_a as case_contact
727 FROM civicrm_case
728 INNER JOIN civicrm_case_contact cc on cc.case_id = civicrm_case.id
729 LEFT JOIN civicrm_case_type ON civicrm_case.case_type_id = civicrm_case_type.id
730 LEFT JOIN civicrm_option_group option_group_case_status ON ( option_group_case_status.name = 'case_status' )
731 LEFT JOIN civicrm_option_value case_status ON ( civicrm_case.status_id = case_status.value
732 AND option_group_case_status.id = case_status.option_group_id )
733 LEFT JOIN civicrm_relationship case_relationship ON ( case_relationship.case_id = civicrm_case.id
734 AND case_relationship.contact_id_a = {$userID})
735 WHERE is_deleted = 0 AND cc.contact_id IN (SELECT id FROM civicrm_contact WHERE is_deleted <> 1)
736{$myCaseWhereClauseA} {$myGroupByClauseA}";
6a488035 737
33621c4f 738 $res = CRM_Core_DAO::executeQuery($query);
6a488035 739 while ($res->fetch()) {
8cc574cf 740 if (!empty($rows[$res->case_type]) && !empty($rows[$res->case_type][$res->case_status])) {
6a488035
TO
741 $rows[$res->case_type][$res->case_status]['count'] = $rows[$res->case_type][$res->case_status]['count'] + 1;
742 }
743 else {
fc9f05e0 744 $rows[$res->case_type][$res->case_status] = [
6a488035
TO
745 'count' => 1,
746 'url' => CRM_Utils_System::url('civicrm/case/search',
747 "reset=1&force=1&status={$res->status_id}&type={$res->case_type_id}&case_owner={$case_owner}"
748 ),
fc9f05e0 749 ];
6a488035
TO
750 }
751 }
752 $caseSummary['rows'] = array_merge($caseTypes, $rows);
753
754 return $caseSummary;
755 }
756
757 /**
d2e5d2ce 758 * Get Case roles.
6a488035 759 *
64bd5a0e
TO
760 * @param int $contactID
761 * Contact id.
762 * @param int $caseID
763 * Case id.
100fef9d 764 * @param int $relationshipID
0fd841b0 765 * @param bool $activeOnly
77b97be7 766 *
a6c01b45
CW
767 * @return array
768 * case role / relationships
6a488035 769 *
6a488035 770 */
0fd841b0 771 public static function getCaseRoles($contactID, $caseID, $relationshipID = NULL, $activeOnly = TRUE) {
6a488035 772 $query = '
3b1c37fe
CW
773 SELECT rel.id as civicrm_relationship_id,
774 con.sort_name as sort_name,
6a488035
TO
775 civicrm_email.email as email,
776 civicrm_phone.phone as phone,
3b1c37fe
CW
777 con.id as civicrm_contact_id,
778 IF(rel.contact_id_a = %1, civicrm_relationship_type.label_a_b, civicrm_relationship_type.label_b_a) as relation,
779 civicrm_relationship_type.id as relation_type,
780 IF(rel.contact_id_a = %1, "a_b", "b_a") as relationship_direction
781 FROM civicrm_relationship rel
782 INNER JOIN civicrm_relationship_type ON rel.relationship_type_id = civicrm_relationship_type.id
211fe764 783 INNER JOIN civicrm_contact con ON ((con.id <> %1 AND con.id IN (rel.contact_id_a, rel.contact_id_b)) OR (con.id = %1 AND rel.contact_id_b = rel.contact_id_a AND rel.contact_id_a = %1 AND rel.is_active))
3b1c37fe
CW
784 LEFT JOIN civicrm_phone ON (civicrm_phone.contact_id = con.id AND civicrm_phone.is_primary = 1)
785 LEFT JOIN civicrm_email ON (civicrm_email.contact_id = con.id AND civicrm_email.is_primary = 1)
786 WHERE (rel.contact_id_a = %1 OR rel.contact_id_b = %1) AND rel.case_id = %2
0fd841b0
JP
787 AND con.is_deleted = 0';
788
789 if ($activeOnly) {
790 $query .= ' AND rel.is_active = 1 AND (rel.end_date IS NULL OR rel.end_date > NOW())';
791 }
6a488035 792
fc9f05e0 793 $params = [
794 1 => [$contactID, 'Positive'],
795 2 => [$caseID, 'Positive'],
796 ];
6a488035
TO
797
798 if ($relationshipID) {
a4bac981 799 $query .= ' AND rel.id = %3 ';
fc9f05e0 800 $params[3] = [$relationshipID, 'Integer'];
6a488035
TO
801 }
802 $dao = CRM_Core_DAO::executeQuery($query, $params);
803
fc9f05e0 804 $values = [];
6a488035
TO
805 while ($dao->fetch()) {
806 $rid = $dao->civicrm_relationship_id;
807 $values[$rid]['cid'] = $dao->civicrm_contact_id;
808 $values[$rid]['relation'] = $dao->relation;
809 $values[$rid]['name'] = $dao->sort_name;
810 $values[$rid]['email'] = $dao->email;
811 $values[$rid]['phone'] = $dao->phone;
812 $values[$rid]['relation_type'] = $dao->relation_type;
813 $values[$rid]['rel_id'] = $dao->civicrm_relationship_id;
3b1c37fe
CW
814 $values[$rid]['client_id'] = $contactID;
815 $values[$rid]['relationship_direction'] = $dao->relationship_direction;
6a488035
TO
816 }
817
6a488035
TO
818 return $values;
819 }
820
821 /**
d2e5d2ce 822 * Get Case Activities.
6a488035 823 *
64bd5a0e
TO
824 * @param int $caseID
825 * Case id.
826 * @param array $params
827 * Posted params.
828 * @param int $contactID
829 * Contact id.
6a488035 830 *
77b97be7 831 * @param null $context
100fef9d 832 * @param int $userID
359d4010 833 * @param null $type (deprecated)
77b97be7 834 *
a6c01b45 835 * @return array
16b10e64 836 * Array of case activities
6a488035 837 *
6a488035 838 */
00be9182 839 public static function getCaseActivity($caseID, &$params, $contactID, $context = NULL, $userID = NULL, $type = NULL) {
44f817d4 840 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
9e74e3ce 841 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
e96f025a 842 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
034500d4 843 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
9e74e3ce 844
6a488035
TO
845 // CRM-5081 - formatting the dates to omit seconds.
846 // Note the 00 in the date format string is needed otherwise later on it thinks scheduled ones are overdue.
ad280fb6 847 $select = "
3636b520 848 SELECT SQL_CALC_FOUND_ROWS COUNT(ca.id) AS ismultiple,
849 ca.id AS id,
850 ca.activity_type_id AS type,
851 ca.activity_type_id AS activity_type_id,
852 tcc.sort_name AS target_contact_name,
853 tcc.id AS target_contact_id,
854 scc.sort_name AS source_contact_name,
855 scc.id AS source_contact_id,
856 acc.sort_name AS assignee_contact_name,
857 acc.id AS assignee_contact_id,
858 DATE_FORMAT(
859 IF(ca.activity_date_time < NOW() AND ca.status_id=ov.value,
860 ca.activity_date_time,
861 DATE_ADD(NOW(), INTERVAL 1 YEAR)
862 ), '%Y%m%d%H%i00') AS overdue_date,
863 DATE_FORMAT(ca.activity_date_time, '%Y%m%d%H%i00') AS display_date,
864 ca.status_id AS status,
865 ca.subject AS subject,
866 ca.is_deleted AS deleted,
867 ca.priority_id AS priority,
868 ca.weight AS weight,
ad280fb6 869 GROUP_CONCAT(ef.file_id) AS attachment_ids ";
6a488035 870
e96f025a 871 $from = "
ad280fb6
JL
872 FROM civicrm_case_activity cca
873 INNER JOIN civicrm_activity ca
874 ON ca.id = cca.activity_id
875 INNER JOIN civicrm_activity_contact cas
876 ON cas.activity_id = ca.id
877 AND cas.record_type_id = {$sourceID}
878 INNER JOIN civicrm_contact scc
879 ON scc.id = cas.contact_id
880 LEFT JOIN civicrm_activity_contact caa
881 ON caa.activity_id = ca.id
882 AND caa.record_type_id = {$assigneeID}
883 LEFT JOIN civicrm_contact acc
884 ON acc.id = caa.contact_id
885 LEFT JOIN civicrm_activity_contact cat
886 ON cat.activity_id = ca.id
887 AND cat.record_type_id = {$targetID}
888 LEFT JOIN civicrm_contact tcc
889 ON tcc.id = cat.contact_id
890 INNER JOIN civicrm_option_group cog
891 ON cog.name = 'activity_type'
892 INNER JOIN civicrm_option_value cov
893 ON cov.option_group_id = cog.id
894 AND cov.value = ca.activity_type_id
895 AND cov.is_active = 1
896 LEFT JOIN civicrm_entity_file ef
897 ON ef.entity_table = 'civicrm_activity'
898 AND ef.entity_id = ca.id
899 LEFT OUTER JOIN civicrm_option_group og
900 ON og.name = 'activity_status'
901 LEFT OUTER JOIN civicrm_option_value ov
902 ON ov.option_group_id=og.id
903 AND ov.name = 'Scheduled'";
904
905 $where = '
906 WHERE cca.case_id= %1
907 AND ca.is_current_revision = 1';
908
909 if (!empty($params['source_contact_id'])) {
910 $where .= "
911 AND cas.contact_id = " . CRM_Utils_Type::escape($params['source_contact_id'], 'Integer');
6a488035
TO
912 }
913
a7488080 914 if (!empty($params['status_id'])) {
ad280fb6
JL
915 $where .= "
916 AND ca.status_id = " . CRM_Utils_Type::escape($params['status_id'], 'Integer');
6a488035
TO
917 }
918
a7488080 919 if (!empty($params['activity_deleted'])) {
ad280fb6
JL
920 $where .= "
921 AND ca.is_deleted = 1";
6a488035
TO
922 }
923 else {
ad280fb6
JL
924 $where .= "
925 AND ca.is_deleted = 0";
6a488035
TO
926 }
927
a7488080 928 if (!empty($params['activity_type_id'])) {
ad280fb6
JL
929 $where .= "
930 AND ca.activity_type_id = " . CRM_Utils_Type::escape($params['activity_type_id'], 'Integer');
6a488035
TO
931 }
932
a7488080 933 if (!empty($params['activity_date_low'])) {
6a488035
TO
934 $fromActivityDate = CRM_Utils_Type::escape(CRM_Utils_Date::processDate($params['activity_date_low']), 'Date');
935 }
ad280fb6
JL
936 if (!empty($fromActivityDate)) {
937 $where .= "
938 AND ca.activity_date_time >= '{$fromActivityDate}'";
939 }
940
a7488080 941 if (!empty($params['activity_date_high'])) {
6a488035
TO
942 $toActivityDate = CRM_Utils_Type::escape(CRM_Utils_Date::processDate($params['activity_date_high']), 'Date');
943 $toActivityDate = $toActivityDate ? $toActivityDate + 235959 : NULL;
944 }
6a488035 945 if (!empty($toActivityDate)) {
ad280fb6
JL
946 $where .= "
947 AND ca.activity_date_time <= '{$toActivityDate}'";
6a488035
TO
948 }
949
3636b520 950 $groupBy = "
951 GROUP BY ca.id, tcc.id, scc.id, acc.id, ov.value";
6a488035 952
ad280fb6
JL
953 $sortBy = CRM_Utils_Array::value('sortBy', $params);
954 if (!$sortBy) {
6a488035 955 // CRM-5081 - added id to act like creation date
ad280fb6
JL
956 $orderBy = "
957 ORDER BY overdue_date ASC, display_date DESC, weight DESC";
6a488035
TO
958 }
959 else {
ad280fb6
JL
960 $sortBy = CRM_Utils_Type::escape($sortBy, 'String');
961 $orderBy = " ORDER BY $sortBy ";
6a488035
TO
962 }
963
3fff685f
JL
964 $page = CRM_Utils_Array::value('page', $params);
965 $rp = CRM_Utils_Array::value('rp', $params);
966
6a488035 967 if (!$page) {
6a488035 968 $page = 1;
6a488035
TO
969 }
970 if (!$rp) {
971 $rp = 10;
972 }
6a488035 973 $start = (($page - 1) * $rp);
ad280fb6 974 $limit = " LIMIT $start, $rp";
6a488035 975
ad280fb6 976 $query = $select . $from . $where . $groupBy . $orderBy . $limit;
fc9f05e0 977 $queryParams = [1 => [$caseID, 'Integer']];
6a488035 978
3fff685f 979 $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
24963ae3 980 $caseCount = CRM_Core_DAO::singleValueQuery('SELECT FOUND_ROWS()');
ad280fb6 981
e96f025a 982 $activityTypes = CRM_Case_PseudoConstant::caseActivityType(FALSE, TRUE);
6a488035 983
0dba8ce1
JP
984 $compStatusValues = array_keys(
985 CRM_Activity_BAO_Activity::getStatusesByType(CRM_Activity_BAO_Activity::COMPLETED) +
986 CRM_Activity_BAO_Activity::getStatusesByType(CRM_Activity_BAO_Activity::CANCELLED)
987 );
ad280fb6 988
6a488035 989 if (!$userID) {
830bab40 990 $userID = CRM_Core_Session::getLoggedInContactID();
6a488035
TO
991 }
992
830bab40 993 $caseActivities = [];
ad280fb6 994
6a488035 995 while ($dao->fetch()) {
ad280fb6 996 $caseActivityId = $dao->id;
6a488035 997
830bab40
MWMC
998 //Do we have permission to access given case activity record.
999 if (!self::checkPermission($caseActivityId, 'view', $dao->activity_type_id, $userID)) {
6a488035
TO
1000 continue;
1001 }
1002
359d4010 1003 $caseActivities[$caseActivityId]['DT_RowId'] = $caseActivityId;
ad280fb6 1004 //Add classes to the row, via DataTables syntax
359d4010 1005 $caseActivities[$caseActivityId]['DT_RowClass'] = "crm-entity status-id-$dao->status";
6a488035 1006
ad280fb6 1007 if (CRM_Utils_Array::crmInArray($dao->status, $compStatusValues)) {
359d4010 1008 $caseActivities[$caseActivityId]['DT_RowClass'] .= " status-completed";
6a488035 1009 }
ad280fb6
JL
1010 else {
1011 if (CRM_Utils_Date::overdue($dao->display_date)) {
359d4010 1012 $caseActivities[$caseActivityId]['DT_RowClass'] .= " status-overdue";
ad280fb6
JL
1013 }
1014 else {
359d4010 1015 $caseActivities[$caseActivityId]['DT_RowClass'] .= " status-scheduled";
6a488035
TO
1016 }
1017 }
ad280fb6
JL
1018
1019 if (!empty($dao->priority)) {
0571e734 1020 if ($dao->priority == CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'priority_id', 'Urgent')) {
359d4010 1021 $caseActivities[$caseActivityId]['DT_RowClass'] .= " priority-urgent ";
ad280fb6 1022 }
0571e734 1023 elseif ($dao->priority == CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'priority_id', 'Low')) {
359d4010 1024 $caseActivities[$caseActivityId]['DT_RowClass'] .= " priority-low ";
6a488035 1025 }
6a488035 1026 }
6a488035 1027
ad280fb6 1028 //Add data to the row for inline editing, via DataTable syntax
fc9f05e0 1029 $caseActivities[$caseActivityId]['DT_RowAttr'] = [];
359d4010
MW
1030 $caseActivities[$caseActivityId]['DT_RowAttr']['data-entity'] = 'activity';
1031 $caseActivities[$caseActivityId]['DT_RowAttr']['data-id'] = $caseActivityId;
6a488035 1032
ad280fb6 1033 //Activity Date and Time
359d4010 1034 $caseActivities[$caseActivityId]['activity_date_time'] = CRM_Utils_Date::customFormat($dao->display_date);
6a488035 1035
ad280fb6 1036 //Activity Subject
359d4010 1037 $caseActivities[$caseActivityId]['subject'] = $dao->subject;
ad280fb6
JL
1038
1039 //Activity Type
359d4010 1040 $caseActivities[$caseActivityId]['type'] = (!empty($activityTypes[$dao->type]['icon']) ? '<span class="crm-i ' . $activityTypes[$dao->type]['icon'] . '"></span> ' : '')
8c99c0bb 1041 . $activityTypes[$dao->type]['label'];
ad280fb6 1042
359d4010
MW
1043 // Activity Target (With Contact) (There can be more than one)
1044 $targetContact = self::formatContactLink($dao->target_contact_id, $dao->target_contact_name);
1045 if (empty($caseActivities[$caseActivityId]['target_contact_name'])) {
1046 $caseActivities[$caseActivityId]['target_contact_name'] = $targetContact;
1047 }
1048 else {
1049 if (strpos($caseActivities[$caseActivityId]['target_contact_name'], $targetContact) === FALSE) {
1050 $caseActivities[$caseActivityId]['target_contact_name'] .= '; ' . $targetContact;
6a488035 1051 }
ad280fb6 1052 }
ad280fb6 1053
359d4010
MW
1054 // Activity Source Contact (Reporter) (There can only be one)
1055 $sourceContact = self::formatContactLink($dao->source_contact_id, $dao->source_contact_name);
1056 $caseActivities[$caseActivityId]['source_contact_name'] = $sourceContact;
1057
1058 // Activity Assignee (There can be more than one)
1059 $assigneeContact = self::formatContactLink($dao->assignee_contact_id, $dao->assignee_contact_name);
1060 if (empty($caseActivities[$caseActivityId]['assignee_contact_name'])) {
1061 $caseActivities[$caseActivityId]['assignee_contact_name'] = $assigneeContact;
ad280fb6 1062 }
359d4010
MW
1063 else {
1064 if (strpos($caseActivities[$caseActivityId]['assignee_contact_name'], $assigneeContact) === FALSE) {
1065 $caseActivities[$caseActivityId]['assignee_contact_name'] .= '; ' . $assigneeContact;
6a488035
TO
1066 }
1067 }
ad280fb6 1068
b864360d 1069 // Activity Status Label for Case activities list
c1c1b937
MWMC
1070 $deleted = '';
1071 if ($dao->deleted) {
1072 $deleted = '<br /> ' . ts('(deleted)');
1073 }
1074 $caseActivities[$caseActivityId]['status_id'] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'activity_status_id', $dao->status) . $deleted;
1075 // if there are file attachments we will return how many
1076 if (!empty($dao->attachment_ids)) {
1077 $attachmentIDs = array_unique(explode(',', $dao->attachment_ids));
1078 $caseActivity['no_attachments'] = count($attachmentIDs);
1079 }
ad280fb6 1080
7bf3b68c
MWMC
1081 $caseActivities[$caseActivityId]['links']
1082 = CRM_Case_Selector_Search::addCaseActivityLinks($caseID, $contactID, $userID, $context, $dao);
6a488035 1083 }
6a488035 1084
fc9f05e0 1085 $caseActivitiesDT = [];
359d4010 1086 $caseActivitiesDT['data'] = array_values($caseActivities);
3fff685f
JL
1087 $caseActivitiesDT['recordsTotal'] = $caseCount;
1088 $caseActivitiesDT['recordsFiltered'] = $caseCount;
ad280fb6
JL
1089
1090 return $caseActivitiesDT;
6a488035
TO
1091 }
1092
359d4010
MW
1093 /**
1094 * Helper function to generate a formatted contact link/name for display in the Case activities tab
1095 *
1096 * @param $contactId
1097 * @param $contactName
1098 *
1099 * @return string
1100 */
1101 private static function formatContactLink($contactId, $contactName) {
1102 if (empty($contactId)) {
1103 return NULL;
1104 }
1105
1106 $hasViewContact = CRM_Contact_BAO_Contact_Permission::allow($contactId);
1107
1108 if ($hasViewContact) {
1109 $contactViewUrl = CRM_Utils_System::url("civicrm/contact/view", "reset=1&cid={$contactId}");
1110 return "<a href=\"{$contactViewUrl}\">" . $contactName . "</a>";
1111 }
1112 else {
1113 return $contactName;
1114 }
1115 }
1116
6a488035 1117 /**
d2e5d2ce 1118 * Get Case Related Contacts.
6a488035 1119 *
64bd5a0e
TO
1120 * @param int $caseID
1121 * Case id.
b982dca0 1122 * @param bool $includeDetails
64bd5a0e 1123 * If true include details of contacts.
6a488035 1124 *
a6c01b45
CW
1125 * @return array
1126 * array of return properties
6a488035 1127 *
6a488035 1128 */
b982dca0 1129 public static function getRelatedContacts($caseID, $includeDetails = TRUE) {
fc9f05e0 1130 $caseRoles = [];
b982dca0 1131 if ($includeDetails) {
fc9f05e0 1132 $caseInfo = civicrm_api3('Case', 'getsingle', [
69f9c562 1133 'id' => $caseID,
b982dca0 1134 // Most efficient way of retrieving definition is to also include case type id and name so the api doesn't have to look it up separately
fc9f05e0 1135 'return' => ['case_type_id', 'case_type_id.name', 'case_type_id.definition', 'contact_id'],
1136 ]);
69f9c562
CW
1137 if (!empty($caseInfo['case_type_id.definition']['caseRoles'])) {
1138 $caseRoles = CRM_Utils_Array::rekey($caseInfo['case_type_id.definition']['caseRoles'], 'name');
1139 }
1140 }
6a488035 1141
fc9f05e0 1142 $values = [];
41cf58d3
AF
1143 $query = <<<HERESQL
1144 SELECT cc.display_name as name, cc.sort_name as sort_name, cc.id, cr.relationship_type_id, crt.label_b_a as role, crt.name_b_a as role_name, ce.email, cp.phone
1145 FROM civicrm_relationship cr
1146 JOIN civicrm_relationship_type crt
1147 ON crt.id = cr.relationship_type_id
1148 JOIN civicrm_contact cc
1149 ON cc.id = cr.contact_id_a
1150 AND cc.is_deleted <> 1
1151 LEFT JOIN civicrm_email ce
1152 ON ce.contact_id = cc.id
1153 AND ce.is_primary= 1
1154 LEFT JOIN civicrm_phone cp
1155 ON cp.contact_id = cc.id
1156 AND cp.is_primary= 1
1157 WHERE cr.case_id = %1
1158 AND cr.is_active
1159 AND cc.id NOT IN (%2)
1160 UNION
1161 SELECT cc.display_name as name, cc.sort_name as sort_name, cc.id, cr.relationship_type_id, crt.label_a_b as role, crt.name_a_b as role_name, ce.email, cp.phone
1162 FROM civicrm_relationship cr
1163 JOIN civicrm_relationship_type crt
1164 ON crt.id = cr.relationship_type_id
1165 JOIN civicrm_contact cc
1166 ON cc.id = cr.contact_id_b
1167 AND cc.is_deleted <> 1
1168 LEFT JOIN civicrm_email ce
1169 ON ce.contact_id = cc.id
1170 AND ce.is_primary= 1
1171 LEFT JOIN civicrm_phone cp
1172 ON cp.contact_id = cc.id
1173 AND cp.is_primary= 1
1174 WHERE cr.case_id = %1
1175 AND cr.is_active
1176 AND cc.id NOT IN (%2)
1177HERESQL;
fc9f05e0 1178 $params = [
1179 1 => [$caseID, 'Integer'],
1180 2 => [implode(',', $caseInfo['client_id']), 'String'],
1181 ];
6a488035
TO
1182 $dao = CRM_Core_DAO::executeQuery($query, $params);
1183
1184 while ($dao->fetch()) {
b982dca0 1185 if (!$includeDetails) {
6a488035
TO
1186 $values[$dao->id] = 1;
1187 }
1188 else {
fc9f05e0 1189 $details = [
6a488035
TO
1190 'contact_id' => $dao->id,
1191 'display_name' => $dao->name,
1192 'sort_name' => $dao->sort_name,
69f9c562 1193 'relationship_type_id' => $dao->relationship_type_id,
6a488035
TO
1194 'role' => $dao->role,
1195 'email' => $dao->email,
a0c7081b 1196 'phone' => $dao->phone,
fc9f05e0 1197 ];
b982dca0 1198 // Add more info about the role (creator, manager)
41cf58d3 1199 $role = CRM_Utils_Array::value($dao->role_name, $caseRoles);
69f9c562
CW
1200 if ($role) {
1201 unset($role['name']);
1202 $details += $role;
1203 }
1204 $values[] = $details;
6a488035
TO
1205 }
1206 }
6a488035
TO
1207
1208 return $values;
1209 }
1210
1211 /**
100fef9d 1212 * Send e-mail copy of activity
6a488035 1213 *
100fef9d 1214 * @param int $clientId
64bd5a0e
TO
1215 * @param int $activityId
1216 * Activity Id.
1217 * @param array $contacts
1218 * Array of related contact.
6a488035 1219 *
77b97be7 1220 * @param null $attachments
100fef9d 1221 * @param int $caseId
77b97be7 1222 *
67d19299 1223 * @return bool |array
6a488035 1224 */
00be9182 1225 public static function sendActivityCopy($clientId, $activityId, $contacts, $attachments = NULL, $caseId) {
6a488035 1226 if (!$activityId) {
67d19299 1227 return FALSE;
6a488035
TO
1228 }
1229
fc9f05e0 1230 $tplParams = $activityInfo = [];
8d04ae48
AP
1231 $activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id');
1232 // If it's a case activity
6a488035 1233 if ($caseId) {
6a488035 1234 $nonCaseActivityTypes = CRM_Core_PseudoConstant::activityType();
a7488080 1235 if (!empty($nonCaseActivityTypes[$activityTypeId])) {
6a488035
TO
1236 $anyActivity = TRUE;
1237 }
1238 else {
1239 $anyActivity = FALSE;
1240 }
1241 $tplParams['isCaseActivity'] = 1;
1242 $tplParams['client_id'] = $clientId;
1243 }
1244 else {
1245 $anyActivity = TRUE;
1246 }
1247
1248 $xmlProcessorProcess = new CRM_Case_XMLProcessor_Process();
1249 $isRedact = $xmlProcessorProcess->getRedactActivityEmail();
1250
1251 $xmlProcessorReport = new CRM_Case_XMLProcessor_Report();
1252
1253 $activityInfo = $xmlProcessorReport->getActivityInfo($clientId, $activityId, $anyActivity, $isRedact);
1254 if ($caseId) {
fc9f05e0 1255 $activityInfo['fields'][] = ['label' => 'Case ID', 'type' => 'String', 'value' => $caseId];
6a488035 1256 }
8d04ae48 1257 $tplParams['activityTypeName'] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_DAO_Activity', 'activity_type_id', $activityTypeId);
6a488035
TO
1258 $tplParams['activity'] = $activityInfo;
1259 foreach ($tplParams['activity']['fields'] as $k => $val) {
1260 if (CRM_Utils_Array::value('label', $val) == ts('Subject')) {
1261 $activitySubject = $val['value'];
1262 break;
1263 }
1264 }
1265 $session = CRM_Core_Session::singleton();
1266 // CRM-8926 If user is not logged in, use the activity creator as userID
1267 if (!($userID = $session->get('userID'))) {
4322672b 1268 $userID = CRM_Activity_BAO_Activity::getSourceContactID($activityId);
6a488035
TO
1269 }
1270
1271 //also create activities simultaneously of this copy.
fc9f05e0 1272 $activityParams = [];
6a488035
TO
1273
1274 $activityParams['source_record_id'] = $activityId;
1275 $activityParams['source_contact_id'] = $userID;
95a718e1 1276 $activityParams['activity_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_type_id', 'Email');
6a488035 1277 $activityParams['activity_date_time'] = date('YmdHis');
95a718e1
JP
1278 $activityParams['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_status_id', 'Completed');
1279 $activityParams['medium_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'encounter_medium', 'email');
6a488035
TO
1280 $activityParams['case_id'] = $caseId;
1281 $activityParams['is_auto'] = 0;
1282 $activityParams['target_id'] = $clientId;
1283
1284 $tplParams['activitySubject'] = $activitySubject;
1285
1286 // if it’s a case activity, add hashed id to the template (CRM-5916)
1287 if ($caseId) {
1288 $tplParams['idHash'] = substr(sha1(CIVICRM_SITE_KEY . $caseId), 0, 7);
1289 }
1290
fc9f05e0 1291 $result = [];
8aeeea00
EH
1292 // CRM-20308 get receiptFrom defaults see https://issues.civicrm.org/jira/browse/CRM-20308
1293 $receiptFrom = self::getReceiptFrom($activityId);
6a488035 1294
fc9f05e0 1295 $recordedActivityParams = [];
6a488035
TO
1296
1297 foreach ($contacts as $mail => $info) {
1298 $tplParams['contact'] = $info;
1299 self::buildPermissionLinks($tplParams, $activityParams);
1300
21827a41 1301 $displayName = CRM_Utils_Array::value('display_name', $info);
6a488035 1302
c6327d7d 1303 list($result[CRM_Utils_Array::value('contact_id', $info)], $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate(
fc9f05e0 1304 [
6a488035
TO
1305 'groupName' => 'msg_tpl_workflow_case',
1306 'valueName' => 'case_activity',
21827a41 1307 'contactId' => CRM_Utils_Array::value('contact_id', $info),
6a488035
TO
1308 'tplParams' => $tplParams,
1309 'from' => $receiptFrom,
1310 'toName' => $displayName,
1311 'toEmail' => $mail,
1312 'attachments' => $attachments,
fc9f05e0 1313 ]
6a488035
TO
1314 );
1315
898f01ea 1316 $activityParams['subject'] = ts('%1 - copy sent to %2', [1 => $activitySubject, 2 => $displayName]);
6a488035
TO
1317 $activityParams['details'] = $message;
1318
21827a41 1319 if (!empty($result[$info['contact_id']])) {
6a488035
TO
1320 /*
1321 * Really only need to record one activity with all the targets combined.
1322 * Originally the template was going to possibly have different content, e.g. depending on permissions,
1323 * but it's always the same content at the moment.
1324 */
1325 if (empty($recordedActivityParams)) {
1326 $recordedActivityParams = $activityParams;
1327 }
1328 else {
1329 $recordedActivityParams['subject'] .= "; $displayName";
1330 }
1331 $recordedActivityParams['target_contact_id'][] = $info['contact_id'];
1332 }
1333 else {
21827a41 1334 unset($result[CRM_Utils_Array::value('contact_id', $info)]);
6a488035
TO
1335 }
1336 }
1337
1338 if (!empty($recordedActivityParams)) {
1339 $activity = CRM_Activity_BAO_Activity::create($recordedActivityParams);
1340
1341 //create case_activity record if its case activity.
1342 if ($caseId) {
fc9f05e0 1343 $caseParams = [
6a488035
TO
1344 'activity_id' => $activity->id,
1345 'case_id' => $caseId,
fc9f05e0 1346 ];
6a488035
TO
1347 self::processCaseActivity($caseParams);
1348 }
1349 }
1350
1351 return $result;
1352 }
1353
1354 /**
1355 * Retrieve count of activities having a particular type, and
1356 * associated with a particular case.
1357 *
64bd5a0e
TO
1358 * @param int $caseId
1359 * ID of the case.
1360 * @param int $activityTypeId
1361 * ID of the activity type.
6a488035
TO
1362 *
1363 * @return array
6a488035 1364 */
00be9182 1365 public static function getCaseActivityCount($caseId, $activityTypeId) {
fc9f05e0 1366 $queryParam = [
1367 1 => [$caseId, 'Integer'],
1368 2 => [$activityTypeId, 'Integer'],
1369 ];
6a488035
TO
1370 $query = "SELECT count(ca.id) as countact
1371 FROM civicrm_activity ca
1372 INNER JOIN civicrm_case_activity cca ON ca.id = cca.activity_id
1373 WHERE ca.activity_type_id = %2
1374 AND cca.case_id = %1
1375 AND ca.is_deleted = 0";
1376
1377 $dao = CRM_Core_DAO::executeQuery($query, $queryParam);
1378 if ($dao->fetch()) {
1379 return $dao->countact;
1380 }
1381
1382 return FALSE;
1383 }
1384
1385 /**
d2e5d2ce 1386 * Create an activity for a case via email.
6a488035 1387 *
64bd5a0e
TO
1388 * @param int $file
1389 * Email sent.
6a488035 1390 *
72b3a70c
CW
1391 * @return array|void
1392 * $activity object of newly creted activity via email
6a488035 1393 */
00be9182 1394 public static function recordActivityViaEmail($file) {
6a488035
TO
1395 if (!file_exists($file) ||
1396 !is_readable($file)
1397 ) {
1398 return CRM_Core_Error::fatal(ts('File %1 does not exist or is not readable',
fc9f05e0 1399 [1 => $file]
d6f468d3 1400 ));
6a488035
TO
1401 }
1402
1403 $result = CRM_Utils_Mail_Incoming::parse($file);
1404 if ($result['is_error']) {
1405 return $result;
1406 }
1407
1408 foreach ($result['to'] as $to) {
1409 $caseId = NULL;
1410
1411 $emailPattern = '/^([A-Z0-9._%+-]+)\+([\d]+)@[A-Z0-9.-]+\.[A-Z]{2,4}$/i';
1412 $replacement = preg_replace($emailPattern, '$2', $to['email']);
1413
1414 if ($replacement !== $to['email']) {
1415 $caseId = $replacement;
1416 //if caseId is invalid, return as error file
1417 if (!CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseId, 'id')) {
1418 return CRM_Core_Error::createAPIError(ts('Invalid case ID ( %1 ) in TO: field.',
fc9f05e0 1419 [1 => $caseId]
d6f468d3 1420 ));
6a488035
TO
1421 }
1422 }
1423 else {
1424 continue;
1425 }
1426
1427 // TODO: May want to replace this with a call to getRelatedAndGlobalContacts() when this feature is revisited.
1428 // (Or for efficiency call the global one outside the loop and then union with this each time.)
b982dca0 1429 $contactDetails = self::getRelatedContacts($caseId, FALSE);
6a488035 1430
a7488080 1431 if (!empty($contactDetails[$result['from']['id']])) {
fc9f05e0 1432 $params = [];
6a488035
TO
1433 $params['subject'] = $result['subject'];
1434 $params['activity_date_time'] = $result['date'];
1435 $params['details'] = $result['body'];
1436 $params['source_contact_id'] = $result['from']['id'];
9c248a42 1437 $params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed');
6a488035
TO
1438
1439 $details = CRM_Case_PseudoConstant::caseActivityType();
fc9f05e0 1440 $matches = [];
6a488035
TO
1441 preg_match('/^\W+([a-zA-Z0-9_ ]+)(\W+)?\n/i',
1442 $result['body'], $matches
1443 );
1444
1445 if (!empty($matches) && isset($matches[1])) {
1446 $activityType = trim($matches[1]);
1447 if (isset($details[$activityType])) {
1448 $params['activity_type_id'] = $details[$activityType]['id'];
1449 }
1450 }
1451 if (!isset($params['activity_type_id'])) {
9c248a42 1452 $params['activity_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Inbound Email');
6a488035
TO
1453 }
1454
1455 // create activity
1456 $activity = CRM_Activity_BAO_Activity::create($params);
1457
fc9f05e0 1458 $caseParams = [
6a488035
TO
1459 'activity_id' => $activity->id,
1460 'case_id' => $caseId,
fc9f05e0 1461 ];
6a488035
TO
1462 self::processCaseActivity($caseParams);
1463 }
1464 else {
1465 return CRM_Core_Error::createAPIError(ts('FROM email contact %1 doesn\'t have a relationship to the referenced case.',
fc9f05e0 1466 [1 => $result['from']['email']]
d6f468d3 1467 ));
6a488035
TO
1468 }
1469 }
1470 }
1471
1472 /**
d2e5d2ce 1473 * Retrieve the scheduled activity type and date.
6a488035 1474 *
64bd5a0e
TO
1475 * @param array $cases
1476 * Array of contact and case id.
77b97be7
EM
1477 *
1478 * @param string $type
6a488035 1479 *
a6c01b45
CW
1480 * @return array
1481 * Array of scheduled activity type and date
6a488035 1482 *
6a488035 1483 *
6a488035 1484 */
00be9182 1485 public static function getNextScheduledActivity($cases, $type = 'upcoming') {
6a488035
TO
1486 $session = CRM_Core_Session::singleton();
1487 $userID = $session->get('userID');
1488
1489 $caseID = implode(',', $cases['case_id']);
1490 $contactID = implode(',', $cases['contact_id']);
1491
5f1c8c57 1492 $condition = " civicrm_case_contact.contact_id IN( {$contactID} )
6a488035
TO
1493 AND civicrm_case.id IN( {$caseID})
1494 AND civicrm_case.is_deleted = {$cases['case_deleted']}";
1495
2ff620ae 1496 $query = self::getCaseActivityQuery($type, $userID, $condition);
57d38398 1497 $activityTypes = CRM_Activity_BAO_Activity::buildOptions('activity_type_id');
6a488035 1498
33621c4f 1499 $res = CRM_Core_DAO::executeQuery($query);
6a488035 1500
fc9f05e0 1501 $activityInfo = [];
6a488035
TO
1502 while ($res->fetch()) {
1503 if ($type == 'upcoming') {
2c783cce 1504 $activityInfo[$res->case_id]['date'] = $res->activity_date_time;
57d38398 1505 $activityInfo[$res->case_id]['type'] = CRM_Utils_Array::value($res->activity_type_id, $activityTypes);
6a488035
TO
1506 }
1507 else {
2c783cce 1508 $activityInfo[$res->case_id]['date'] = $res->activity_date_time;
57d38398 1509 $activityInfo[$res->case_id]['type'] = CRM_Utils_Array::value($res->activity_type_id, $activityTypes);
6a488035
TO
1510 }
1511 }
1512
1513 return $activityInfo;
1514 }
1515
1516 /**
d2e5d2ce 1517 * Combine all the exportable fields from the lower levels object.
6a488035 1518 *
a6c01b45
CW
1519 * @return array
1520 * array of exportable Fields
6a488035 1521 */
00be9182 1522 public static function &exportableFields() {
6a488035
TO
1523 if (!self::$_exportableFields) {
1524 if (!self::$_exportableFields) {
fc9f05e0 1525 self::$_exportableFields = [];
6a488035
TO
1526 }
1527
e96f025a 1528 $fields = CRM_Case_DAO_Case::export();
fc9f05e0 1529 $fields['case_role'] = ['title' => ts('Role in Case')];
1530 $fields['case_type'] = [
e96f025a 1531 'title' => ts('Case Type'),
6a488035 1532 'name' => 'case_type',
fc9f05e0 1533 ];
1534 $fields['case_status'] = [
e96f025a 1535 'title' => ts('Case Status'),
6a488035 1536 'name' => 'case_status',
fc9f05e0 1537 ];
6a488035 1538
e75182f2
JJ
1539 // add custom data for cases
1540 $fields = array_merge($fields, CRM_Core_BAO_CustomField::getFieldsForImport('Case'));
1541
6a488035
TO
1542 self::$_exportableFields = $fields;
1543 }
1544 return self::$_exportableFields;
1545 }
1546
1547 /**
d2e5d2ce 1548 * Restore the record that are associated with this case.
6a488035 1549 *
64bd5a0e
TO
1550 * @param int $caseId
1551 * Id of the case to restore.
6a488035 1552 *
72b3a70c 1553 * @return bool
6a488035 1554 */
00be9182 1555 public static function restoreCase($caseId) {
6a488035
TO
1556 //restore activities
1557 $activities = self::getCaseActivityDates($caseId);
1558 if ($activities) {
1559 foreach ($activities as $value) {
1560 CRM_Activity_BAO_Activity::restoreActivity($value);
1561 }
1562 }
1563 //restore case
e96f025a 1564 $case = new CRM_Case_DAO_Case();
1565 $case->id = $caseId;
6a488035
TO
1566 $case->is_deleted = 0;
1567 $case->save();
1568
1569 //CRM-7364, enable relationships
1570 self::enableDisableCaseRelationships($caseId, TRUE);
1571 return TRUE;
1572 }
1573
4c6ce474
EM
1574 /**
1575 * @param $groupInfo
1576 * @param null $sort
1577 * @param null $showLinks
1578 * @param bool $returnOnlyCount
1579 * @param int $offset
1580 * @param int $rowCount
1581 *
1582 * @return array
1583 */
00be9182 1584 public static function getGlobalContacts(&$groupInfo, $sort = NULL, $showLinks = NULL, $returnOnlyCount = FALSE, $offset = 0, $rowCount = 25) {
fc9f05e0 1585 $globalContacts = [];
6a488035
TO
1586
1587 $settingsProcessor = new CRM_Case_XMLProcessor_Settings();
1588 $settings = $settingsProcessor->run();
1589 if (!empty($settings)) {
1590 $groupInfo['name'] = $settings['groupname'];
1591 if ($groupInfo['name']) {
fc9f05e0 1592 $searchParams = ['name' => $groupInfo['name']];
1593 $results = [];
6a488035
TO
1594 CRM_Contact_BAO_Group::retrieve($searchParams, $results);
1595 if ($results) {
e96f025a 1596 $groupInfo['id'] = $results['id'];
6a488035 1597 $groupInfo['title'] = $results['title'];
fc9f05e0 1598 $params = [['group', '=', $groupInfo['id'], 0, 0]];
1599 $return = ['contact_id' => 1, 'sort_name' => 1, 'display_name' => 1, 'email' => 1, 'phone' => 1];
d79c94d5 1600 list($globalContacts) = CRM_Contact_BAO_Query::apiQuery($params, $return, NULL, $sort, $offset, $rowCount, TRUE, $returnOnlyCount);
6a488035
TO
1601
1602 if ($returnOnlyCount) {
1603 return $globalContacts;
1604 }
1605
1606 if ($showLinks) {
e96f025a 1607 foreach ($globalContacts as $idx => $contact) {
d79c94d5 1608 $globalContacts[$idx]['sort_name'] = '<a href="' . CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$contact['contact_id']}") . '">' . $contact['sort_name'] . '</a>';
6a488035
TO
1609 }
1610 }
1611 }
1612 }
1613 }
1614 return $globalContacts;
1615 }
1616
4c6ce474 1617 /**
d2e5d2ce 1618 * Convenience function to get both case contacts and global in one array.
fc9f05e0 1619 *
100fef9d 1620 * @param int $caseId
4c6ce474
EM
1621 *
1622 * @return array
1623 */
00be9182 1624 public static function getRelatedAndGlobalContacts($caseId) {
6a488035
TO
1625 $relatedContacts = self::getRelatedContacts($caseId);
1626
fc9f05e0 1627 $groupInfo = [];
6a488035
TO
1628 $globalContacts = self::getGlobalContacts($groupInfo);
1629
1630 //unset values which are not required.
1631 foreach ($globalContacts as $k => & $v) {
1632 unset($v['email_id']);
1633 unset($v['group_contact_id']);
1634 unset($v['status']);
1635 unset($v['phone']);
1636 $v['role'] = $groupInfo['title'];
1637 }
1638 //include multiple listings for the same contact/different roles.
1639 $relatedGlobalContacts = array_merge($relatedContacts, $globalContacts);
1640 return $relatedGlobalContacts;
1641 }
1642
1643 /**
100fef9d 1644 * Get Case ActivitiesDueDates with given criteria.
6a488035 1645 *
64bd5a0e
TO
1646 * @param int $caseID
1647 * Case id.
1648 * @param array $criteriaParams
1649 * Given criteria.
1650 * @param bool $latestDate
72b3a70c 1651 * If set newest or oldest date is selected.
6a488035 1652 *
72b3a70c
CW
1653 * @return array
1654 * case activities due dates
6a488035 1655 *
6a488035 1656 */
fc9f05e0 1657 public static function getCaseActivityDates($caseID, $criteriaParams = [], $latestDate = FALSE) {
1658 $values = [];
6a488035 1659 $selectDate = " ca.activity_date_time";
e96f025a 1660 $where = $groupBy = ' ';
6a488035
TO
1661
1662 if (!$caseID) {
408b79bf 1663 return NULL;
6a488035
TO
1664 }
1665
1666 if ($latestDate) {
a7488080 1667 if (!empty($criteriaParams['activity_type_id'])) {
6a488035
TO
1668 $where .= " AND ca.activity_type_id = " . CRM_Utils_Type::escape($criteriaParams['activity_type_id'], 'Integer');
1669 $where .= " AND ca.is_current_revision = 1";
e5cceea5 1670 $groupBy .= " GROUP BY ca.activity_type_id, ca.id";
6a488035
TO
1671 }
1672
a7488080 1673 if (!empty($criteriaParams['newest'])) {
6a488035
TO
1674 $selectDate = " max(ca.activity_date_time) ";
1675 }
1676 else {
1677 $selectDate = " min(ca.activity_date_time) ";
1678 }
1679 }
1680
1681 $query = "SELECT ca.id, {$selectDate} as activity_date
1682 FROM civicrm_activity ca
1683 LEFT JOIN civicrm_case_activity cca ON cca.activity_id = ca.id LEFT JOIN civicrm_case cc ON cc.id = cca.case_id
1684 WHERE cc.id = %1 {$where} {$groupBy}";
1685
fc9f05e0 1686 $params = [1 => [$caseID, 'Integer']];
6a488035
TO
1687 $dao = CRM_Core_DAO::executeQuery($query, $params);
1688
1689 while ($dao->fetch()) {
1690 $values[$dao->id]['id'] = $dao->id;
1691 $values[$dao->id]['activity_date'] = $dao->activity_date;
1692 }
6a488035
TO
1693 return $values;
1694 }
1695
1696 /**
100fef9d 1697 * Create activities when Case or Other roles assigned/modified/deleted.
6a488035 1698 *
100fef9d 1699 * @param int $caseId
64bd5a0e
TO
1700 * @param int $relationshipId
1701 * Relationship id.
1702 * @param int $relContactId
1703 * Case role assignee contactId.
100fef9d 1704 * @param int $contactId
6a488035 1705 */
00be9182 1706 public static function createCaseRoleActivity($caseId, $relationshipId, $relContactId = NULL, $contactId = NULL) {
6a488035
TO
1707 if (!$caseId || !$relationshipId || empty($relationshipId)) {
1708 return;
1709 }
1710
fc9f05e0 1711 $queryParam = [];
6a488035
TO
1712 if (is_array($relationshipId)) {
1713 $relationshipId = implode(',', $relationshipId);
1714 $relationshipClause = " civicrm_relationship.id IN ($relationshipId)";
1715 }
1716 else {
1717 $relationshipClause = " civicrm_relationship.id = %1";
fc9f05e0 1718 $queryParam[1] = [$relationshipId, 'Positive'];
6a488035
TO
1719 }
1720
1721 $query = "
1722 SELECT cc.display_name as clientName,
1723 cca.display_name as assigneeContactName,
1724 civicrm_relationship.case_id as caseId,
1725 civicrm_relationship_type.label_a_b as relation_a_b,
1726 civicrm_relationship_type.label_b_a as relation_b_a,
1727 civicrm_relationship.contact_id_b as rel_contact_id,
1728 civicrm_relationship.contact_id_a as assign_contact_id
1729 FROM civicrm_relationship_type, civicrm_relationship
1730 LEFT JOIN civicrm_contact cc ON cc.id = civicrm_relationship.contact_id_b
1731 LEFT JOIN civicrm_contact cca ON cca.id = civicrm_relationship.contact_id_a
1732 WHERE civicrm_relationship.relationship_type_id = civicrm_relationship_type.id AND {$relationshipClause}";
1733
1734 $dao = CRM_Core_DAO::executeQuery($query, $queryParam);
1735
1736 while ($dao->fetch()) {
e3b9b4f6
KW
1737 // The assignee is not the client.
1738 if ($dao->rel_contact_id != $contactId) {
6a488035
TO
1739 $caseRelationship = $dao->relation_a_b;
1740 $assigneContactName = $dao->clientName;
1741 $assigneContactIds[$dao->rel_contact_id] = $dao->rel_contact_id;
1742 }
1743 else {
1744 $caseRelationship = $dao->relation_b_a;
1745 $assigneContactName = $dao->assigneeContactName;
1746 $assigneContactIds[$dao->assign_contact_id] = $dao->assign_contact_id;
1747 }
1748 }
1749
1750 $session = CRM_Core_Session::singleton();
fc9f05e0 1751 $activityParams = [
6a488035
TO
1752 'source_contact_id' => $session->get('userID'),
1753 'subject' => $caseRelationship . ' : ' . $assigneContactName,
1754 'activity_date_time' => date('YmdHis'),
9c248a42 1755 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'),
fc9f05e0 1756 ];
6a488035
TO
1757
1758 //if $relContactId is passed, role is added or modified.
1759 if (!empty($relContactId)) {
1760 $activityParams['assignee_contact_id'] = $assigneContactIds;
9c248a42 1761 $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Assign Case Role');
6a488035
TO
1762 }
1763 else {
9c248a42 1764 $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Remove Case Role');
6a488035
TO
1765 }
1766
1767 $activityParams['activity_type_id'] = $activityTypeID;
1768
1769 $activity = CRM_Activity_BAO_Activity::create($activityParams);
1770
1771 //create case_activity record.
fc9f05e0 1772 $caseParams = [
6a488035
TO
1773 'activity_id' => $activity->id,
1774 'case_id' => $caseId,
fc9f05e0 1775 ];
6a488035
TO
1776
1777 CRM_Case_BAO_Case::processCaseActivity($caseParams);
1778 }
1779
1780 /**
100fef9d 1781 * Get case manger
6a488035
TO
1782 * contact which is assigned a case role of case manager.
1783 *
64bd5a0e
TO
1784 * @param int $caseType
1785 * Case type.
1786 * @param int $caseId
1787 * Case id.
6a488035 1788 *
c00bd201 1789 * @return string
1790 * html hyperlink of manager contact view page
6a488035 1791 *
6a488035 1792 */
00be9182 1793 public static function getCaseManagerContact($caseType, $caseId) {
6a488035 1794 if (!$caseType || !$caseId) {
408b79bf 1795 return NULL;
6a488035
TO
1796 }
1797
5757b086 1798 $caseManagerName = '---';
6a488035
TO
1799 $xmlProcessor = new CRM_Case_XMLProcessor_Process();
1800
1801 $managerRoleId = $xmlProcessor->getCaseManagerRoleId($caseType);
1802
1803 if (!empty($managerRoleId)) {
41cf58d3
AF
1804 if (substr($managerRoleId, -4) == '_a_b') {
1805 $managerRoleQuery = "
1806 SELECT civicrm_contact.id as casemanager_id,
1807 civicrm_contact.sort_name as casemanager
1808 FROM civicrm_contact
1809 LEFT JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = civicrm_contact.id AND civicrm_relationship.relationship_type_id = %1) AND civicrm_relationship.is_active
1810 LEFT JOIN civicrm_case ON civicrm_case.id = civicrm_relationship.case_id
1811 WHERE civicrm_case.id = %2 AND is_active = 1";
1812 }
1813 if (substr($managerRoleId, -4) == '_b_a') {
1814 $managerRoleQuery = "
1815 SELECT civicrm_contact.id as casemanager_id,
1816 civicrm_contact.sort_name as casemanager
1817 FROM civicrm_contact
1818 LEFT JOIN civicrm_relationship ON (civicrm_relationship.contact_id_a = civicrm_contact.id AND civicrm_relationship.relationship_type_id = %1) AND civicrm_relationship.is_active
1819 LEFT JOIN civicrm_case ON civicrm_case.id = civicrm_relationship.case_id
1820 WHERE civicrm_case.id = %2 AND is_active = 1";
1821 }
6a488035 1822
fc9f05e0 1823 $managerRoleParams = [
1824 1 => [substr($managerRoleId, 0, -4), 'Integer'],
1825 2 => [$caseId, 'Integer'],
1826 ];
6a488035
TO
1827
1828 $dao = CRM_Core_DAO::executeQuery($managerRoleQuery, $managerRoleParams);
1829 if ($dao->fetch()) {
5757b086 1830 $caseManagerName = sprintf('<a href="%s">%s</a>',
fc9f05e0 1831 CRM_Utils_System::url('civicrm/contact/view', ['cid' => $dao->casemanager_id]),
c00bd201 1832 $dao->casemanager
1833 );
6a488035
TO
1834 }
1835 }
5757b086 1836
1837 return $caseManagerName;
6a488035
TO
1838 }
1839
4c6ce474 1840 /**
100fef9d 1841 * @param int $contactId
4c6ce474
EM
1842 * @param bool $excludeDeleted
1843 *
eeb45e43 1844 * @return int
4c6ce474 1845 */
00be9182 1846 public static function caseCount($contactId = NULL, $excludeDeleted = TRUE) {
fc9f05e0 1847 $params = ['check_permissions' => TRUE];
6a488035 1848 if ($excludeDeleted) {
0a1a8b63 1849 $params['is_deleted'] = 0;
6a488035
TO
1850 }
1851 if ($contactId) {
0a1a8b63 1852 $params['contact_id'] = $contactId;
6a488035 1853 }
eeb45e43
CW
1854 try {
1855 return civicrm_api3('Case', 'getcount', $params);
1856 }
1857 catch (CiviCRM_API3_Exception $e) {
1858 // Lack of permissions will throw an exception
1859 return 0;
1860 }
6a488035
TO
1861 }
1862
1863 /**
6e19e2ea 1864 * Retrieve related case ids for given case.
6a488035 1865 *
6e19e2ea 1866 * @param int $caseId
64bd5a0e
TO
1867 * @param bool $excludeDeleted
1868 * Do not include deleted cases.
6a488035 1869 *
72b3a70c 1870 * @return array
6a488035 1871 */
6e19e2ea 1872 public static function getRelatedCaseIds($caseId, $excludeDeleted = TRUE) {
6a488035
TO
1873 //FIXME : do check for permissions.
1874
6e19e2ea 1875 if (!$caseId) {
fc9f05e0 1876 return [];
6a488035
TO
1877 }
1878
b864360d 1879 $linkActType = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Link Cases');
6a488035 1880 if (!$linkActType) {
fc9f05e0 1881 return [];
6a488035
TO
1882 }
1883
1884 $whereClause = "mainCase.id = %2";
1885 if ($excludeDeleted) {
1886 $whereClause .= " AND ( relAct.is_deleted = 0 OR relAct.is_deleted IS NULL )";
1887 }
1888
6a488035
TO
1889 $query = "
1890 SELECT relCaseAct.case_id
1891 FROM civicrm_case mainCase
1892 INNER JOIN civicrm_case_activity mainCaseAct ON (mainCaseAct.case_id = mainCase.id)
1893 INNER JOIN civicrm_activity mainAct ON (mainCaseAct.activity_id = mainAct.id AND mainAct.activity_type_id = %1)
1894 INNER JOIN civicrm_case_activity relCaseAct ON (relCaseAct.activity_id = mainAct.id AND mainCaseAct.id != relCaseAct.id)
1895 INNER JOIN civicrm_activity relAct ON (relCaseAct.activity_id = relAct.id AND relAct.activity_type_id = %1)
1896 WHERE $whereClause";
1897
fc9f05e0 1898 $dao = CRM_Core_DAO::executeQuery($query, [
1899 1 => [$linkActType, 'Integer'],
1900 2 => [$caseId, 'Integer'],
1901 ]);
1902 $relatedCaseIds = [];
6a488035
TO
1903 while ($dao->fetch()) {
1904 $relatedCaseIds[$dao->case_id] = $dao->case_id;
1905 }
6a488035 1906
6e19e2ea
CW
1907 return array_values($relatedCaseIds);
1908 }
1909
1910 /**
1911 * Retrieve related case details for given case.
1912 *
1913 * @param int $caseId
1914 * @param bool $excludeDeleted
1915 * Do not include deleted cases.
1916 *
1917 * @return array
1918 */
1919 public static function getRelatedCases($caseId, $excludeDeleted = TRUE) {
1920 $relatedCaseIds = self::getRelatedCaseIds($caseId, $excludeDeleted);
fc9f05e0 1921 $relatedCases = [];
6e19e2ea
CW
1922
1923 if (!$relatedCaseIds) {
fc9f05e0 1924 return [];
6a488035
TO
1925 }
1926
1927 $whereClause = 'relCase.id IN ( ' . implode(',', $relatedCaseIds) . ' )';
1928 if ($excludeDeleted) {
1929 $whereClause .= " AND ( relCase.is_deleted = 0 OR relCase.is_deleted IS NULL )";
1930 }
1931
1932 //filter for permissioned cases.
fc9f05e0 1933 $filterCases = [];
6a488035
TO
1934 $doFilterCases = FALSE;
1935 if (!CRM_Core_Permission::check('access all cases and activities')) {
1936 $doFilterCases = TRUE;
5f1c8c57 1937 $filterCases = CRM_Case_BAO_Case::getCases(FALSE);
6a488035
TO
1938 }
1939
1940 //2. fetch the details of related cases.
1941 $query = "
1942 SELECT relCase.id as id,
8ffdec17 1943 civicrm_case_type.title as case_type,
6a488035 1944 client.display_name as client_name,
74b15fae
CR
1945 client.id as client_id,
1946 relCase.status_id
6a488035
TO
1947 FROM civicrm_case relCase
1948 INNER JOIN civicrm_case_contact relCaseContact ON ( relCase.id = relCaseContact.case_id )
1949 INNER JOIN civicrm_contact client ON ( client.id = relCaseContact.contact_id )
8ffdec17 1950 LEFT JOIN civicrm_case_type ON relCase.case_type_id = civicrm_case_type.id
6a488035
TO
1951 WHERE {$whereClause}";
1952
e96f025a 1953 $dao = CRM_Core_DAO::executeQuery($query);
6a488035
TO
1954 $contactViewUrl = CRM_Utils_System::url("civicrm/contact/view", "reset=1&cid=");
1955 $hasViewContact = CRM_Core_Permission::giveMeAllACLs();
74b15fae 1956 $statuses = CRM_Case_BAO_Case::buildOptions('status_id');
6a488035
TO
1957
1958 while ($dao->fetch()) {
1959 $caseView = NULL;
1960 if (!$doFilterCases || array_key_exists($dao->id, $filterCases)) {
1961 $caseViewStr = "reset=1&id={$dao->id}&cid={$dao->client_id}&action=view&context=case&selectedChild=case";
1962 $caseViewUrl = CRM_Utils_System::url("civicrm/contact/view/case", $caseViewStr);
6ce08914 1963 $caseView = "<a class='action-item no-popup crm-hover-button' href='{$caseViewUrl}'>" . ts('View Case') . "</a>";
6a488035
TO
1964 }
1965 $clientView = $dao->client_name;
1966 if ($hasViewContact) {
1967 $clientView = "<a href='{$contactViewUrl}{$dao->client_id}'>$dao->client_name</a>";
1968 }
1969
fc9f05e0 1970 $relatedCases[$dao->id] = [
6a488035
TO
1971 'case_id' => $dao->id,
1972 'case_type' => $dao->case_type,
1973 'client_name' => $clientView,
74b15fae 1974 'case_status' => $statuses[$dao->status_id],
6a488035 1975 'links' => $caseView,
fc9f05e0 1976 ];
6a488035 1977 }
6a488035
TO
1978
1979 return $relatedCases;
1980 }
1981
1982 /**
1983 * Merge two duplicate contacts' cases - follow CRM-5758 rules.
1984 *
fc9f05e0 1985 * @param int $mainContactId
1986 * @param int $otherContactId
1987 *
6a488035
TO
1988 * @see CRM_Dedupe_Merger::cpTables()
1989 *
1990 * TODO: use the 3rd $sqls param to append sql statements rather than executing them here
cde2037d 1991 *
6a488035 1992 */
00be9182 1993 public static function mergeContacts($mainContactId, $otherContactId) {
6a488035
TO
1994 self::mergeCases($mainContactId, NULL, $otherContactId);
1995 }
1996
1997 /**
1998 * Function perform two task.
1999 * 1. Merge two duplicate contacts cases - follow CRM-5758 rules.
2000 * 2. Merge two cases of same contact - follow CRM-5598 rules.
2001 *
64bd5a0e
TO
2002 * @param int $mainContactId
2003 * Contact id of main contact record.
2004 * @param int $mainCaseId
2005 * Case id of main case record.
2006 * @param int $otherContactId
2007 * Contact id of record which is going to merge.
2008 * @param int $otherCaseId
2009 * Case id of record which is going to merge.
77b97be7
EM
2010 *
2011 * @param bool $changeClient
6a488035 2012 *
e97c66ff 2013 * @return int|null
2014 * @throws \CRM_Core_Exception
6a488035 2015 */
a130e045 2016 public static function mergeCases(
28d4d481
TO
2017 $mainContactId, $mainCaseId = NULL, $otherContactId = NULL,
2018 $otherCaseId = NULL, $changeClient = FALSE) {
6a488035
TO
2019 $moveToTrash = TRUE;
2020
2021 $duplicateContacts = FALSE;
2022 if ($mainContactId && $otherContactId &&
2023 $mainContactId != $otherContactId
2024 ) {
2025 $duplicateContacts = TRUE;
2026 }
2027
2028 $duplicateCases = FALSE;
2029 if ($mainCaseId && $otherCaseId &&
2030 $mainCaseId != $otherCaseId
2031 ) {
2032 $duplicateCases = TRUE;
2033 }
2034
fc9f05e0 2035 $mainCaseIds = [];
6a488035
TO
2036 if (!$duplicateContacts && !$duplicateCases) {
2037 return $mainCaseIds;
2038 }
2039
b864360d
MWMC
2040 $activityTypes = CRM_Activity_BAO_Activity::buildOptions('activity_type_id', 'validate');
2041 $completedActivityStatus = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed');
44f817d4 2042 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
9e74e3ce 2043 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
2044 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
2045 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
8ef12e64 2046
fc9f05e0 2047 $processCaseIds = [$otherCaseId];
6a488035
TO
2048 if ($duplicateContacts && !$duplicateCases) {
2049 if ($changeClient) {
fc9f05e0 2050 $processCaseIds = [$mainCaseId];
6a488035
TO
2051 }
2052 else {
2053 //get all case ids for other contact.
2054 $processCaseIds = self::retrieveCaseIdsByContactId($otherContactId, TRUE);
2055 }
2056 if (!is_array($processCaseIds)) {
2057 return;
2058 }
2059 }
2060
2061 $session = CRM_Core_Session::singleton();
2062 $currentUserId = $session->get('userID');
2063
02094cdb
JJ
2064 CRM_Utils_Hook::pre_case_merge($mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient);
2065
6a488035
TO
2066 // copy all cases and connect to main contact id.
2067 foreach ($processCaseIds as $otherCaseId) {
2068 if ($duplicateContacts) {
fc9f05e0 2069 $mainCase = CRM_Core_DAO::copyGeneric('CRM_Case_DAO_Case', ['id' => $otherCaseId]);
6a488035
TO
2070 $mainCaseId = $mainCase->id;
2071 if (!$mainCaseId) {
2072 continue;
2073 }
8bd86283 2074
6a488035
TO
2075 $mainCaseIds[] = $mainCaseId;
2076 //insert record for case contact.
2077 $otherCaseContact = new CRM_Case_DAO_CaseContact();
2078 $otherCaseContact->case_id = $otherCaseId;
2079 $otherCaseContact->find();
2080 while ($otherCaseContact->fetch()) {
2081 $mainCaseContact = new CRM_Case_DAO_CaseContact();
2082 $mainCaseContact->case_id = $mainCaseId;
2083 $mainCaseContact->contact_id = $otherCaseContact->contact_id;
2084 if ($mainCaseContact->contact_id == $otherContactId) {
2085 $mainCaseContact->contact_id = $mainContactId;
2086 }
2087 //avoid duplicate object.
2088 if (!$mainCaseContact->find(TRUE)) {
2089 $mainCaseContact->save();
2090 }
6a488035 2091 }
6a488035
TO
2092 }
2093 elseif (!$otherContactId) {
2094 $otherContactId = $mainContactId;
2095 }
2096
2097 if (!$mainCaseId || !$otherCaseId ||
2098 !$mainContactId || !$otherContactId
2099 ) {
2100 continue;
2101 }
2102
2103 // get all activities for other case.
fc9f05e0 2104 $otherCaseActivities = [];
6a488035
TO
2105 CRM_Core_DAO::commonRetrieveAll('CRM_Case_DAO_CaseActivity', 'case_id', $otherCaseId, $otherCaseActivities);
2106
2107 //for duplicate cases do not process singleton activities.
fc9f05e0 2108 $otherActivityIds = $singletonActivityIds = [];
6a488035
TO
2109 foreach ($otherCaseActivities as $caseActivityId => $otherIds) {
2110 $otherActId = CRM_Utils_Array::value('activity_id', $otherIds);
2111 if (!$otherActId || in_array($otherActId, $otherActivityIds)) {
2112 continue;
2113 }
2114 $otherActivityIds[] = $otherActId;
2115 }
2116 if ($duplicateCases) {
2117 if ($openCaseType = array_search('Open Case', $activityTypes)) {
2118 $sql = "
2119SELECT id
2120 FROM civicrm_activity
2121 WHERE activity_type_id = $openCaseType
2122 AND id IN ( " . implode(',', array_values($otherActivityIds)) . ');';
2123 $dao = CRM_Core_DAO::executeQuery($sql);
2124 while ($dao->fetch()) {
2125 $singletonActivityIds[] = $dao->id;
2126 }
6a488035
TO
2127 }
2128 }
2129
2130 // migrate all activities and connect to main contact.
fc9f05e0 2131 $copiedActivityIds = $activityMappingIds = [];
6a488035
TO
2132 sort($otherActivityIds);
2133 foreach ($otherActivityIds as $otherActivityId) {
2134
2135 //for duplicate cases -
2136 //do not migrate singleton activities.
2137 if (!$otherActivityId || in_array($otherActivityId, $singletonActivityIds)) {
2138 continue;
2139 }
2140
2141 //migrate activity record.
2142 $otherActivity = new CRM_Activity_DAO_Activity();
2143 $otherActivity->id = $otherActivityId;
2144 if (!$otherActivity->find(TRUE)) {
2145 continue;
2146 }
2147
fc9f05e0 2148 $mainActVals = [];
6a488035
TO
2149 $mainActivity = new CRM_Activity_DAO_Activity();
2150 CRM_Core_DAO::storeValues($otherActivity, $mainActVals);
2151 $mainActivity->copyValues($mainActVals);
2152 $mainActivity->id = NULL;
2153 $mainActivity->activity_date_time = CRM_Utils_Date::isoToMysql($otherActivity->activity_date_time);
6a488035
TO
2154 $mainActivity->source_record_id = CRM_Utils_Array::value($mainActivity->source_record_id,
2155 $activityMappingIds
2156 );
2157
2158 $mainActivity->original_id = CRM_Utils_Array::value($mainActivity->original_id,
2159 $activityMappingIds
2160 );
2161
2162 $mainActivity->parent_id = CRM_Utils_Array::value($mainActivity->parent_id,
2163 $activityMappingIds
2164 );
2165 $mainActivity->save();
2166 $mainActivityId = $mainActivity->id;
2167 if (!$mainActivityId) {
2168 continue;
2169 }
2170
2171 $activityMappingIds[$otherActivityId] = $mainActivityId;
4322672b 2172 // insert log of all activities
6a488035
TO
2173 CRM_Activity_BAO_Activity::logActivityAction($mainActivity);
2174
6a488035
TO
2175 $copiedActivityIds[] = $otherActivityId;
2176
2177 //create case activity record.
2178 $mainCaseActivity = new CRM_Case_DAO_CaseActivity();
2179 $mainCaseActivity->case_id = $mainCaseId;
2180 $mainCaseActivity->activity_id = $mainActivityId;
2181 $mainCaseActivity->save();
6a488035 2182
4322672b 2183 //migrate source activity.
2184 $otherSourceActivity = new CRM_Activity_DAO_ActivityContact();
2185 $otherSourceActivity->activity_id = $otherActivityId;
2186 $otherSourceActivity->record_type_id = $sourceID;
2187 $otherSourceActivity->find();
2188 while ($otherSourceActivity->fetch()) {
2189 $mainActivitySource = new CRM_Activity_DAO_ActivityContact();
2190 $mainActivitySource->record_type_id = $sourceID;
2191 $mainActivitySource->activity_id = $mainActivityId;
2192 $mainActivitySource->contact_id = $otherSourceActivity->contact_id;
2193 if ($mainActivitySource->contact_id == $otherContactId) {
2194 $mainActivitySource->contact_id = $mainContactId;
2195 }
2196 //avoid duplicate object.
2197 if (!$mainActivitySource->find(TRUE)) {
2198 $mainActivitySource->save();
2199 }
4322672b 2200 }
4322672b 2201
6a488035 2202 //migrate target activities.
4e3d3cfc 2203 $otherTargetActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2204 $otherTargetActivity->activity_id = $otherActivityId;
9e74e3ce 2205 $otherTargetActivity->record_type_id = $targetID;
6a488035
TO
2206 $otherTargetActivity->find();
2207 while ($otherTargetActivity->fetch()) {
4e3d3cfc 2208 $mainActivityTarget = new CRM_Activity_DAO_ActivityContact();
9e74e3ce 2209 $mainActivityTarget->record_type_id = $targetID;
6a488035 2210 $mainActivityTarget->activity_id = $mainActivityId;
00bf7e59 2211 $mainActivityTarget->contact_id = $otherTargetActivity->contact_id;
2212 if ($mainActivityTarget->contact_id == $otherContactId) {
2213 $mainActivityTarget->contact_id = $mainContactId;
6a488035
TO
2214 }
2215 //avoid duplicate object.
2216 if (!$mainActivityTarget->find(TRUE)) {
2217 $mainActivityTarget->save();
2218 }
6a488035 2219 }
6a488035
TO
2220
2221 //migrate assignee activities.
4e3d3cfc 2222 $otherAssigneeActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2223 $otherAssigneeActivity->activity_id = $otherActivityId;
9e74e3ce 2224 $otherAssigneeActivity->record_type_id = $assigneeID;
6a488035
TO
2225 $otherAssigneeActivity->find();
2226 while ($otherAssigneeActivity->fetch()) {
4e3d3cfc 2227 $mainAssigneeActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2228 $mainAssigneeActivity->activity_id = $mainActivityId;
9e74e3ce 2229 $mainAssigneeActivity->record_type_id = $assigneeID;
00bf7e59 2230 $mainAssigneeActivity->contact_id = $otherAssigneeActivity->contact_id;
2231 if ($mainAssigneeActivity->contact_id == $otherContactId) {
2232 $mainAssigneeActivity->contact_id = $mainContactId;
6a488035
TO
2233 }
2234 //avoid duplicate object.
2235 if (!$mainAssigneeActivity->find(TRUE)) {
2236 $mainAssigneeActivity->save();
2237 }
6a488035 2238 }
8c31eef5
D
2239
2240 // copy custom fields and attachments
fc9f05e0 2241 $aparams = [
4322672b 2242 'activityID' => $otherActivityId,
2243 'mainActivityId' => $mainActivityId,
fc9f05e0 2244 ];
8c31eef5 2245 CRM_Activity_BAO_Activity::copyExtendedActivityData($aparams);
6a488035
TO
2246 }
2247
2248 //copy case relationship.
2249 if ($duplicateContacts) {
2250 //migrate relationship records.
2251 $otherRelationship = new CRM_Contact_DAO_Relationship();
2252 $otherRelationship->case_id = $otherCaseId;
2253 $otherRelationship->find();
fc9f05e0 2254 $otherRelationshipIds = [];
6a488035 2255 while ($otherRelationship->fetch()) {
fc9f05e0 2256 $otherRelVals = [];
6a488035
TO
2257 $updateOtherRel = FALSE;
2258 CRM_Core_DAO::storeValues($otherRelationship, $otherRelVals);
2259
2260 $mainRelationship = new CRM_Contact_DAO_Relationship();
2261 $mainRelationship->copyValues($otherRelVals);
2262 $mainRelationship->id = NULL;
2263 $mainRelationship->case_id = $mainCaseId;
2264 if ($mainRelationship->contact_id_a == $otherContactId) {
2265 $updateOtherRel = TRUE;
2266 $mainRelationship->contact_id_a = $mainContactId;
2267 }
2268
2269 //case creator change only when we merge user contact.
2270 if ($mainRelationship->contact_id_b == $otherContactId) {
2271 //do not change creator for change client.
2272 if (!$changeClient) {
2273 $updateOtherRel = TRUE;
2274 $mainRelationship->contact_id_b = ($currentUserId) ? $currentUserId : $mainContactId;
2275 }
2276 }
2277 $mainRelationship->end_date = CRM_Utils_Date::isoToMysql($otherRelationship->end_date);
2278 $mainRelationship->start_date = CRM_Utils_Date::isoToMysql($otherRelationship->start_date);
2279
2280 //avoid duplicate object.
2281 if (!$mainRelationship->find(TRUE)) {
2282 $mainRelationship->save();
2283 }
6a488035
TO
2284
2285 //get the other relationship ids to update end date.
2286 if ($updateOtherRel) {
2287 $otherRelationshipIds[$otherRelationship->id] = $otherRelationship->id;
2288 }
2289 }
6a488035
TO
2290
2291 //update other relationships end dates
2292 if (!empty($otherRelationshipIds)) {
2293 $sql = 'UPDATE civicrm_relationship
2294 SET end_date = CURDATE()
2295 WHERE id IN ( ' . implode(',', $otherRelationshipIds) . ')';
2296 CRM_Core_DAO::executeQuery($sql);
2297 }
2298 }
2299
2300 //move other case to trash.
2301 $mergeCase = self::deleteCase($otherCaseId, $moveToTrash);
2302 if (!$mergeCase) {
2303 continue;
2304 }
2305
2306 $mergeActSubject = $mergeActSubjectDetails = $mergeActType = '';
2307 if ($changeClient) {
2308 $mainContactDisplayName = CRM_Contact_BAO_Contact::displayName($mainContactId);
2309 $otherContactDisplayName = CRM_Contact_BAO_Contact::displayName($otherContactId);
2310
2311 $mergeActType = array_search('Reassigned Case', $activityTypes);
2312 $mergeActSubject = ts("Case %1 reassigned client from %2 to %3. New Case ID is %4.",
fc9f05e0 2313 [
e96f025a 2314 1 => $otherCaseId,
2315 2 => $otherContactDisplayName,
2316 3 => $mainContactDisplayName,
21dfd5f5 2317 4 => $mainCaseId,
fc9f05e0 2318 ]
6a488035
TO
2319 );
2320 }
2321 elseif ($duplicateContacts) {
2322 $mergeActType = array_search('Merge Case', $activityTypes);
2323 $mergeActSubject = ts("Case %1 copied from contact id %2 to contact id %3 via merge. New Case ID is %4.",
fc9f05e0 2324 [
e96f025a 2325 1 => $otherCaseId,
2326 2 => $otherContactId,
2327 3 => $mainContactId,
21dfd5f5 2328 4 => $mainCaseId,
fc9f05e0 2329 ]
6a488035
TO
2330 );
2331 }
2332 else {
2333 $mergeActType = array_search('Merge Case', $activityTypes);
fc9f05e0 2334 $mergeActSubject = ts("Case %1 merged into case %2", [1 => $otherCaseId, 2 => $mainCaseId]);
6a488035
TO
2335 if (!empty($copiedActivityIds)) {
2336 $sql = '
2337SELECT id, subject, activity_date_time, activity_type_id
2338FROM civicrm_activity
2339WHERE id IN (' . implode(',', $copiedActivityIds) . ')';
2340 $dao = CRM_Core_DAO::executeQuery($sql);
2341 while ($dao->fetch()) {
2342 $mergeActSubjectDetails .= "{$dao->activity_date_time} :: {$activityTypes[$dao->activity_type_id]}";
2343 if ($dao->subject) {
2344 $mergeActSubjectDetails .= " :: {$dao->subject}";
2345 }
2346 $mergeActSubjectDetails .= "<br />";
2347 }
2348 }
2349 }
2350
b864360d 2351 // Create merge activity record. Source for merge activity is the logged in user's contact ID ($currentUserId).
fc9f05e0 2352 $activityParams = [
6a488035
TO
2353 'subject' => $mergeActSubject,
2354 'details' => $mergeActSubjectDetails,
b864360d 2355 'status_id' => $completedActivityStatus,
6a488035 2356 'activity_type_id' => $mergeActType,
8c677b07 2357 'source_contact_id' => $currentUserId,
6a488035 2358 'activity_date_time' => date('YmdHis'),
fc9f05e0 2359 ];
6a488035
TO
2360
2361 $mergeActivity = CRM_Activity_BAO_Activity::create($activityParams);
2362 $mergeActivityId = $mergeActivity->id;
2363 if (!$mergeActivityId) {
2364 continue;
2365 }
6a488035
TO
2366
2367 //connect merge activity to case.
fc9f05e0 2368 $mergeCaseAct = [
6a488035
TO
2369 'case_id' => $mainCaseId,
2370 'activity_id' => $mergeActivityId,
fc9f05e0 2371 ];
6a488035
TO
2372
2373 self::processCaseActivity($mergeCaseAct);
2374 }
02094cdb
JJ
2375
2376 CRM_Utils_Hook::post_case_merge($mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient);
2377
6a488035
TO
2378 return $mainCaseIds;
2379 }
2380
2381 /**
2382 * Validate contact permission for
2383 * edit/view on activity record and build links.
2384 *
64bd5a0e
TO
2385 * @param array $tplParams
2386 * Params to be sent to template for sending email.
2387 * @param array $activityParams
2388 * Info of the activity.
6a488035 2389 */
00be9182 2390 public static function buildPermissionLinks(&$tplParams, $activityParams) {
6a488035
TO
2391 $activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityParams['source_record_id'],
2392 'activity_type_id', 'id'
2393 );
2394
a7488080 2395 if (!empty($tplParams['isCaseActivity'])) {
6a488035
TO
2396 $tplParams['editActURL'] = CRM_Utils_System::url('civicrm/case/activity',
2397 "reset=1&cid={$activityParams['target_id']}&caseid={$activityParams['case_id']}&action=update&id={$activityParams['source_record_id']}", TRUE
2398 );
2399
2400 $tplParams['viewActURL'] = CRM_Utils_System::url('civicrm/case/activity/view',
2401 "reset=1&aid={$activityParams['source_record_id']}&cid={$activityParams['target_id']}&caseID={$activityParams['case_id']}", TRUE
2402 );
2403
2404 $tplParams['manageCaseURL'] = CRM_Utils_System::url('civicrm/contact/view/case',
2405 "reset=1&id={$activityParams['case_id']}&cid={$activityParams['target_id']}&action=view&context=home", TRUE
2406 );
2407 }
2408 else {
2409 $tplParams['editActURL'] = CRM_Utils_System::url('civicrm/contact/view/activity',
2410 "atype=$activityTypeId&action=update&reset=1&id={$activityParams['source_record_id']}&cid={$tplParams['contact']['contact_id']}&context=activity", TRUE
2411 );
2412
2413 $tplParams['viewActURL'] = CRM_Utils_System::url('civicrm/contact/view/activity',
2414 "atype=$activityTypeId&action=view&reset=1&id={$activityParams['source_record_id']}&cid={$tplParams['contact']['contact_id']}&context=activity", TRUE
2415 );
2416 }
2417 }
2418
2419 /**
2420 * Validate contact permission for
2421 * given operation on activity record.
2422 *
64bd5a0e
TO
2423 * @param int $activityId
2424 * Activity record id.
2425 * @param string $operation
2426 * User operation.
2427 * @param int $actTypeId
2428 * Activity type id.
2429 * @param int $contactId
2430 * Contact id/if not pass consider logged in.
2431 * @param bool $checkComponent
2432 * Do we need to check component enabled.
6a488035 2433 *
a130e045 2434 * @return bool
6a488035 2435 */
00be9182 2436 public static function checkPermission($activityId, $operation, $actTypeId = NULL, $contactId = NULL, $checkComponent = TRUE) {
6a488035
TO
2437 $allow = FALSE;
2438 if (!$actTypeId && $activityId) {
2439 $actTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id');
2440 }
2441
2442 if (!$activityId || !$operation || !$actTypeId) {
2443 return $allow;
2444 }
2445
2446 //do check for civicase component enabled.
077dbf5e
CW
2447 if ($checkComponent && !self::enabled()) {
2448 return $allow;
6a488035
TO
2449 }
2450
2451 //do check for cases.
fc9f05e0 2452 $caseActOperations = [
6a488035
TO
2453 'File On Case',
2454 'Link Cases',
2455 'Move To Case',
2456 'Copy To Case',
fc9f05e0 2457 ];
6a488035
TO
2458
2459 if (in_array($operation, $caseActOperations)) {
abd06efc
CW
2460 static $caseCount;
2461 if (!isset($caseCount)) {
eeb45e43 2462 try {
fc9f05e0 2463 $caseCount = civicrm_api3('Case', 'getcount', [
eeb45e43 2464 'check_permissions' => TRUE,
fc9f05e0 2465 'status_id' => ['!=' => 'Closed'],
eeb45e43 2466 'is_deleted' => 0,
fc9f05e0 2467 'end_date' => ['IS NULL' => 1],
2468 ]);
eeb45e43
CW
2469 }
2470 catch (CiviCRM_API3_Exception $e) {
2471 // Lack of permissions will throw an exception
2472 $caseCount = 0;
2473 }
6a488035
TO
2474 }
2475 if ($operation == 'File On Case') {
abd06efc 2476 $allow = !empty($caseCount);
6a488035
TO
2477 }
2478 else {
abd06efc 2479 $allow = ($caseCount > 1);
6a488035
TO
2480 }
2481 }
2482
fc9f05e0 2483 $actionOperations = ['view', 'edit', 'delete'];
6a488035
TO
2484 if (in_array($operation, $actionOperations)) {
2485
2486 //do cache when user has non/supper permission.
2487 static $allowOperations;
2488
2489 if (!is_array($allowOperations) ||
2490 !array_key_exists($operation, $allowOperations)
2491 ) {
2492
2493 if (!$contactId) {
2494 $session = CRM_Core_Session::singleton();
2495 $contactId = $session->get('userID');
2496 }
2497
2498 //check for permissions.
fc9f05e0 2499 $permissions = [
2500 'view' => [
6a488035
TO
2501 'access my cases and activities',
2502 'access all cases and activities',
fc9f05e0 2503 ],
2504 'edit' => [
6a488035
TO
2505 'access my cases and activities',
2506 'access all cases and activities',
fc9f05e0 2507 ],
2508 'delete' => ['delete activities'],
2509 ];
6a488035
TO
2510
2511 //check for core permission.
fc9f05e0 2512 $hasPermissions = [];
6a488035
TO
2513 $checkPermissions = CRM_Utils_Array::value($operation, $permissions);
2514 if (is_array($checkPermissions)) {
2515 foreach ($checkPermissions as $per) {
2516 if (CRM_Core_Permission::check($per)) {
2517 $hasPermissions[$operation][] = $per;
2518 }
2519 }
2520 }
2521
2522 //has permissions.
2523 if (!empty($hasPermissions)) {
2524 //need to check activity object specific.
fc9f05e0 2525 if (in_array($operation, [
e96f025a 2526 'view',
21dfd5f5 2527 'edit',
fc9f05e0 2528 ])
e96f025a 2529 ) {
6a488035
TO
2530 //do we have supper permission.
2531 if (in_array('access all cases and activities', $hasPermissions[$operation])) {
2532 $allowOperations[$operation] = $allow = TRUE;
2533 }
2534 else {
2535 //user has only access to my cases and activity.
2536 //here object specific permmions come in picture.
2537
2538 //edit - contact must be source or assignee
2539 //view - contact must be source/assignee/target
2540 $isTarget = $isAssignee = $isSource = FALSE;
44f817d4 2541 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
4322672b 2542 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
2543 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
2544 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
6a488035 2545
4e3d3cfc 2546 $target = new CRM_Activity_DAO_ActivityContact();
9e74e3ce 2547 $target->record_type_id = $targetID;
6a488035 2548 $target->activity_id = $activityId;
00bf7e59 2549 $target->contact_id = $contactId;
6a488035
TO
2550 if ($target->find(TRUE)) {
2551 $isTarget = TRUE;
2552 }
2553
4e3d3cfc 2554 $assignee = new CRM_Activity_DAO_ActivityContact();
6a488035 2555 $assignee->activity_id = $activityId;
9e74e3ce 2556 $assignee->record_type_id = $assigneeID;
00bf7e59 2557 $assignee->contact_id = $contactId;
6a488035
TO
2558 if ($assignee->find(TRUE)) {
2559 $isAssignee = TRUE;
2560 }
2561
4322672b 2562 $source = new CRM_Activity_DAO_ActivityContact();
2563 $source->activity_id = $activityId;
2564 $source->record_type_id = $sourceID;
2565 $source->contact_id = $contactId;
2566 if ($source->find(TRUE)) {
6a488035
TO
2567 $isSource = TRUE;
2568 }
2569
2570 if ($operation == 'edit') {
2571 if ($isAssignee || $isSource) {
2572 $allow = TRUE;
2573 }
2574 }
2575 if ($operation == 'view') {
2576 if ($isTarget || $isAssignee || $isSource) {
2577 $allow = TRUE;
2578 }
2579 }
2580 }
2581 }
2582 elseif (is_array($hasPermissions[$operation])) {
2583 $allowOperations[$operation] = $allow = TRUE;
2584 }
2585 }
2586 else {
2587 //contact do not have permission.
2588 $allowOperations[$operation] = FALSE;
2589 }
2590 }
2591 else {
2592 //use cache.
2593 //here contact might have supper/non permission.
2594 $allow = $allowOperations[$operation];
2595 }
2596 }
2597
2598 //do further only when operation is granted.
2599 if ($allow) {
b864360d 2600 $actTypeName = CRM_Core_PseudoConstant::getName('CRM_Activity_BAO_Activity', 'activity_type_id', $actTypeId);
6a488035
TO
2601
2602 //do not allow multiple copy / edit action.
fc9f05e0 2603 $singletonNames = [
e96f025a 2604 'Open Case',
2605 'Reassigned Case',
2606 'Merge Case',
2607 'Link Cases',
2608 'Assign Case Role',
2609 'Email',
21dfd5f5 2610 'Inbound Email',
fc9f05e0 2611 ];
6a488035
TO
2612
2613 //do not allow to delete these activities, CRM-4543
fc9f05e0 2614 $doNotDeleteNames = ['Open Case', 'Change Case Type', 'Change Case Status', 'Change Case Start Date'];
6a488035
TO
2615
2616 //allow edit operation.
fc9f05e0 2617 $allowEditNames = ['Open Case'];
6a488035 2618
426fe3c7 2619 if (CRM_Activity_BAO_Activity::checkEditInboundEmailsPermissions()) {
ee90a98c
CR
2620 $allowEditNames[] = 'Inbound Email';
2621 }
2622
6a488035 2623 // do not allow File on Case
fc9f05e0 2624 $doNotFileNames = [
e96f025a 2625 'Open Case',
2626 'Change Case Type',
2627 'Change Case Status',
2628 'Change Case Start Date',
2629 'Reassigned Case',
2630 'Merge Case',
2631 'Link Cases',
21dfd5f5 2632 'Assign Case Role',
fc9f05e0 2633 ];
6a488035
TO
2634
2635 if (in_array($actTypeName, $singletonNames)) {
2636 $allow = FALSE;
2637 if ($operation == 'File On Case') {
2638 $allow = (in_array($actTypeName, $doNotFileNames)) ? FALSE : TRUE;
2639 }
2640 if (in_array($operation, $actionOperations)) {
2641 $allow = TRUE;
2642 if ($operation == 'edit') {
2643 $allow = (in_array($actTypeName, $allowEditNames)) ? TRUE : FALSE;
2644 }
2645 elseif ($operation == 'delete') {
2646 $allow = (in_array($actTypeName, $doNotDeleteNames)) ? FALSE : TRUE;
2647 }
2648 }
2649 }
2650 if ($allow && ($operation == 'delete') &&
2651 in_array($actTypeName, $doNotDeleteNames)
2652 ) {
2653 $allow = FALSE;
2654 }
2655
2656 if ($allow && ($operation == 'File On Case') &&
2657 in_array($actTypeName, $doNotFileNames)
2658 ) {
2659 $allow = FALSE;
2660 }
2661
2662 //check settings file for masking actions
2663 //on the basis the activity types
2664 //hide Edit link if activity type is NOT editable
2665 //(special case activities).CRM-5871
2666 if ($allow && in_array($operation, $actionOperations)) {
fc9f05e0 2667 static $actionFilter = [];
6a488035
TO
2668 if (!array_key_exists($operation, $actionFilter)) {
2669 $xmlProcessor = new CRM_Case_XMLProcessor_Process();
2670 $actionFilter[$operation] = $xmlProcessor->get('Settings', 'ActivityTypes', FALSE, $operation);
2671 }
2672 if (array_key_exists($operation, $actionFilter[$operation]) &&
2673 in_array($actTypeId, $actionFilter[$operation][$operation])
2674 ) {
2675 $allow = FALSE;
2676 }
2677 }
2678 }
2679
2680 return $allow;
2681 }
2682
2683 /**
100fef9d 2684 * Since we drop 'access CiviCase', allow access
6a488035
TO
2685 * if user has 'access my cases and activities'
2686 * or 'access all cases and activities'
2687 */
00be9182 2688 public static function accessCiviCase() {
077dbf5e 2689 if (!self::enabled()) {
6a488035
TO
2690 return FALSE;
2691 }
2692
2693 if (CRM_Core_Permission::check('access my cases and activities') ||
2694 CRM_Core_Permission::check('access all cases and activities')
2695 ) {
2696 return TRUE;
2697 }
2698
2699 return FALSE;
2700 }
2701
2702 /**
d2e5d2ce 2703 * Verify user has permission to access a case.
077dbf5e
CW
2704 *
2705 * @param int $caseId
64bd5a0e
TO
2706 * @param bool $denyClosed
2707 * Set TRUE if one wants closed cases to be treated as inaccessible.
077dbf5e
CW
2708 *
2709 * @return bool
2710 */
00be9182 2711 public static function accessCase($caseId, $denyClosed = TRUE) {
077dbf5e
CW
2712 if (!$caseId || !self::enabled()) {
2713 return FALSE;
2714 }
2715
fc9f05e0 2716 $params = ['id' => $caseId, 'check_permissions' => TRUE];
3924e596 2717 if ($denyClosed && !CRM_Core_Permission::check('access all cases and activities')) {
fc9f05e0 2718 $params['status_id'] = ['!=' => 'Closed'];
546abeb1 2719 }
eeb45e43
CW
2720 try {
2721 return (bool) civicrm_api3('Case', 'getcount', $params);
2722 }
2723 catch (CiviCRM_API3_Exception $e) {
2724 // Lack of permissions will throw an exception
2725 return FALSE;
2726 }
077dbf5e
CW
2727 }
2728
6a488035 2729 /**
d2e5d2ce 2730 * Check whether activity is a case Activity.
6a488035 2731 *
64bd5a0e
TO
2732 * @param int $activityID
2733 * Activity id.
6a488035 2734 *
a130e045 2735 * @return bool
6a488035 2736 */
00be9182 2737 public static function isCaseActivity($activityID) {
6a488035
TO
2738 $isCaseActivity = FALSE;
2739 if ($activityID) {
fc9f05e0 2740 $params = [1 => [$activityID, 'Integer']];
6a488035
TO
2741 $query = "SELECT id FROM civicrm_case_activity WHERE activity_id = %1";
2742 if (CRM_Core_DAO::singleValueQuery($query, $params)) {
2743 $isCaseActivity = TRUE;
2744 }
2745 }
2746
2747 return $isCaseActivity;
2748 }
2749
2750 /**
d2e5d2ce 2751 * Get all the case type ids currently in use.
6a488035 2752 *
a6c01b45 2753 * @return array
6a488035 2754 */
00be9182 2755 public static function getUsedCaseType() {
6a488035
TO
2756 static $caseTypeIds;
2757
2758 if (!is_array($caseTypeIds)) {
2759 $query = "SELECT DISTINCT( civicrm_case.case_type_id ) FROM civicrm_case";
2760
2761 $dao = CRM_Core_DAO::executeQuery($query);
fc9f05e0 2762 $caseTypeIds = [];
6a488035
TO
2763 while ($dao->fetch()) {
2764 $typeId = explode(CRM_Core_DAO::VALUE_SEPARATOR,
2765 $dao->case_type_id
2766 );
2767 $caseTypeIds[] = $typeId[1];
2768 }
2769 }
2770
2771 return $caseTypeIds;
2772 }
2773
2774 /**
d2e5d2ce 2775 * Get all the case status ids currently in use.
6a488035 2776 *
a6c01b45 2777 * @return array
6a488035 2778 */
00be9182 2779 public static function getUsedCaseStatuses() {
6a488035
TO
2780 static $caseStatusIds;
2781
2782 if (!is_array($caseStatusIds)) {
2783 $query = "SELECT DISTINCT( civicrm_case.status_id ) FROM civicrm_case";
2784
2785 $dao = CRM_Core_DAO::executeQuery($query);
fc9f05e0 2786 $caseStatusIds = [];
6a488035
TO
2787 while ($dao->fetch()) {
2788 $caseStatusIds[] = $dao->status_id;
2789 }
2790 }
2791
2792 return $caseStatusIds;
2793 }
2794
2795 /**
d2e5d2ce 2796 * Get all the encounter medium ids currently in use.
72b3a70c 2797 *
6a488035
TO
2798 * @return array
2799 */
00be9182 2800 public static function getUsedEncounterMediums() {
6a488035
TO
2801 static $mediumIds;
2802
2803 if (!is_array($mediumIds)) {
2804 $query = "SELECT DISTINCT( civicrm_activity.medium_id ) FROM civicrm_activity";
2805
2806 $dao = CRM_Core_DAO::executeQuery($query);
fc9f05e0 2807 $mediumIds = [];
6a488035
TO
2808 while ($dao->fetch()) {
2809 $mediumIds[] = $dao->medium_id;
2810 }
2811 }
2812
2813 return $mediumIds;
2814 }
2815
2816 /**
100fef9d 2817 * Check case configuration.
6a488035 2818 *
100fef9d 2819 * @param int $contactId
77b97be7 2820 *
a6c01b45 2821 * @return array
6a488035 2822 */
00be9182 2823 public static function isCaseConfigured($contactId = NULL) {
fc9f05e0 2824 $configured = array_fill_keys(['configured', 'allowToAddNewCase', 'redirectToCaseAdmin'], FALSE);
6a488035
TO
2825
2826 //lets check for case configured.
2827 $allCasesCount = CRM_Case_BAO_Case::caseCount(NULL, FALSE);
2828 $configured['configured'] = ($allCasesCount) ? TRUE : FALSE;
2829 if (!$configured['configured']) {
2830 //do check for case type and case status.
0372ffa2 2831 $caseTypes = CRM_Case_PseudoConstant::caseType('title', FALSE);
6a488035
TO
2832 if (!empty($caseTypes)) {
2833 $configured['configured'] = TRUE;
2834 if (!$configured['configured']) {
2835 $caseStatuses = CRM_Case_PseudoConstant::caseStatus('label', FALSE);
2836 if (!empty($caseStatuses)) {
2837 $configured['configured'] = TRUE;
2838 }
2839 }
2840 }
2841 }
2842 if ($configured['configured']) {
2843 //do check for active case type and case status.
2844 $caseTypes = CRM_Case_PseudoConstant::caseType();
2845 if (!empty($caseTypes)) {
2846 $caseStatuses = CRM_Case_PseudoConstant::caseStatus();
2847 if (!empty($caseStatuses)) {
2848 $configured['allowToAddNewCase'] = TRUE;
2849 }
2850 }
2851
2852 //do we need to redirect user to case admin.
2853 if (!$configured['allowToAddNewCase'] && $contactId) {
2854 //check for current contact case count.
2855 $currentContatCasesCount = CRM_Case_BAO_Case::caseCount($contactId);
2856 //redirect user to case admin page.
2857 if (!$currentContatCasesCount) {
2858 $configured['redirectToCaseAdmin'] = TRUE;
2859 }
2860 }
2861 }
2862
2863 return $configured;
2864 }
2865
d6f468d3 2866 /**
d2e5d2ce 2867 * Used during case component enablement and during ugprade.
72b3a70c
CW
2868 *
2869 * @return bool
6a488035 2870 */
00be9182 2871 public static function createCaseViews() {
6a4257d4 2872 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
a19fc402
TO
2873 $dao = new CRM_Core_DAO();
2874
6a488035 2875 $sql = self::createCaseViewsQuery('upcoming');
6a488035
TO
2876 $dao->query($sql);
2877 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
2878 return FALSE;
2879 }
2880
2881 // Above error doesn't get caught?
2882 $doublecheck = $dao->singleValueQuery("SELECT count(id) FROM civicrm_view_case_activity_upcoming");
2883 if (is_null($doublecheck)) {
2884 return FALSE;
2885 }
2886
2887 $sql = self::createCaseViewsQuery('recent');
6a488035
TO
2888 $dao->query($sql);
2889 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
2890 return FALSE;
2891 }
2892
2893 // Above error doesn't get caught?
2894 $doublecheck = $dao->singleValueQuery("SELECT count(id) FROM civicrm_view_case_activity_recent");
2895 if (is_null($doublecheck)) {
2896 return FALSE;
2897 }
2898
2899 return TRUE;
2900 }
2901
d6f468d3 2902 /**
100fef9d 2903 * Helper function, also used by the upgrade in case of error
72b3a70c 2904 *
cde2037d
EM
2905 * @param string $section
2906 *
72b3a70c 2907 * @return string
6a488035 2908 */
00be9182 2909 public static function createCaseViewsQuery($section = 'upcoming') {
6a488035 2910 $sql = "";
0db2818f 2911 $scheduled_id = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Scheduled');
6a488035
TO
2912 switch ($section) {
2913 case 'upcoming':
2914 $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_upcoming`
2915 AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
2916 FROM civicrm_case_activity ca
2917 INNER JOIN civicrm_activity a ON ca.activity_id=a.id
66d84429
AH
2918 WHERE a.id =
2919(SELECT b.id FROM civicrm_case_activity bca
2b95a25b 2920 INNER JOIN civicrm_activity b ON bca.activity_id=b.id
2921 WHERE b.activity_date_time <= DATE_ADD( NOW(), INTERVAL 14 DAY )
2922 AND b.is_current_revision = 1 AND b.is_deleted=0 AND b.status_id = $scheduled_id
2923 AND bca.case_id = ca.case_id ORDER BY b.activity_date_time ASC LIMIT 1)";
6a488035
TO
2924 break;
2925
2926 case 'recent':
2927 $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_recent`
2928 AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
2929 FROM civicrm_case_activity ca
2930 INNER JOIN civicrm_activity a ON ca.activity_id=a.id
66d84429
AH
2931 WHERE a.id =
2932(SELECT b.id FROM civicrm_case_activity bca
2b95a25b 2933 INNER JOIN civicrm_activity b ON bca.activity_id=b.id
2934 WHERE b.activity_date_time >= DATE_SUB( NOW(), INTERVAL 14 DAY )
2935 AND b.is_current_revision = 1 AND b.is_deleted=0 AND b.status_id <> $scheduled_id
2936 AND bca.case_id = ca.case_id ORDER BY b.activity_date_time DESC LIMIT 1)";
6a488035
TO
2937 break;
2938 }
6a488035 2939 return $sql;
e96f025a 2940 }
2941
2942 /**
100fef9d 2943 * Add/copy relationships, when new client is added for a case
e96f025a 2944 *
64bd5a0e
TO
2945 * @param int $caseId
2946 * Case id.
2947 * @param int $contactId
2948 * Contact id / new client id.
e96f025a 2949 */
00be9182 2950 public static function addCaseRelationships($caseId, $contactId) {
44466d80
KJ
2951 // get the case role / relationships for the case
2952 $caseRelationships = new CRM_Contact_DAO_Relationship();
2953 $caseRelationships->case_id = $caseId;
2954 $caseRelationships->find();
fc9f05e0 2955 $relationshipTypes = [];
44466d80
KJ
2956
2957 // make sure we don't add duplicate relationships of same relationship type.
2958 while ($caseRelationships->fetch() && !in_array($caseRelationships->relationship_type_id, $relationshipTypes)) {
fc9f05e0 2959 $values = [];
44466d80
KJ
2960 CRM_Core_DAO::storeValues($caseRelationships, $values);
2961
2962 // add relationship for new client.
2963 $newRelationship = new CRM_Contact_DAO_Relationship();
2964 $newRelationship->copyValues($values);
2965 $newRelationship->id = NULL;
2966 $newRelationship->case_id = $caseId;
2967 $newRelationship->contact_id_a = $contactId;
2968 $newRelationship->end_date = CRM_Utils_Date::isoToMysql($caseRelationships->end_date);
2969 $newRelationship->start_date = CRM_Utils_Date::isoToMysql($caseRelationships->start_date);
2970
2971 // another check to avoid duplicate relationship, in cases where client is removed and re-added again.
2972 if (!$newRelationship->find(TRUE)) {
2973 $newRelationship->save();
2974 }
44466d80
KJ
2975
2976 // store relationship type of newly created relationship
2977 $relationshipTypes[] = $caseRelationships->relationship_type_id;
2978 }
6a488035 2979 }
14a679f1
KJ
2980
2981 /**
d2e5d2ce 2982 * Get the list of clients for a case.
14a679f1
KJ
2983 *
2984 * @param int $caseId
2985 *
a6c01b45
CW
2986 * @return array
2987 * associated array with client ids
14a679f1 2988 */
00be9182 2989 public static function getCaseClients($caseId) {
fc9f05e0 2990 $clients = [];
14a679f1
KJ
2991 $caseContact = new CRM_Case_DAO_CaseContact();
2992 $caseContact->case_id = $caseId;
78762324 2993 $caseContact->orderBy('id');
14a679f1
KJ
2994 $caseContact->find();
2995
e96f025a 2996 while ($caseContact->fetch()) {
14a679f1
KJ
2997 $clients[] = $caseContact->contact_id;
2998 }
2999
3000 return $clients;
3001 }
16c0ec8d 3002
3b1c37fe
CW
3003 /**
3004 * @param int $caseId
3005 * @param string $direction
3006 * @param int $cid
3007 * @param int $relTypeId
fc9f05e0 3008 *
3b1c37fe
CW
3009 * @throws \CRM_Core_Exception
3010 * @throws \CiviCRM_API3_Exception
3011 */
3012 public static function endCaseRole($caseId, $direction, $cid, $relTypeId) {
3013 // Validate inputs
3014 if ($direction !== 'a' && $direction !== 'b') {
3015 throw new CRM_Core_Exception('Invalid relationship direction');
3016 }
3017
3018 // This case might have multiple clients, so we lookup by relationship instead of by id to get them all
3019 $sql = "SELECT id FROM civicrm_relationship WHERE case_id = %1 AND contact_id_{$direction} = %2 AND relationship_type_id = %3";
fc9f05e0 3020 $dao = CRM_Core_DAO::executeQuery($sql, [
3021 1 => [$caseId, 'Positive'],
3022 2 => [$cid, 'Positive'],
3023 3 => [$relTypeId, 'Positive'],
3024 ]);
3b1c37fe 3025 while ($dao->fetch()) {
fc9f05e0 3026 civicrm_api3('relationship', 'create', [
3b1c37fe
CW
3027 'id' => $dao->id,
3028 'is_active' => 0,
3029 'end_date' => 'now',
fc9f05e0 3030 ]);
3b1c37fe
CW
3031 }
3032 }
3033
16c0ec8d
CW
3034 /**
3035 * Get options for a given case field.
16c0ec8d 3036 *
64bd5a0e
TO
3037 * @param string $fieldName
3038 * @param string $context
64bd5a0e 3039 * @param array $props
72b3a70c 3040 * Whatever is known about this dao object.
77b97be7 3041 *
a130e045 3042 * @return array|bool
fc9f05e0 3043 * @throws \CiviCRM_API3_Exception
3044 *
3045 * @see CRM_Core_DAO::buildOptionsContext
3046 * @see CRM_Core_DAO::buildOptions
3047 *
16c0ec8d 3048 */
fc9f05e0 3049 public static function buildOptions($fieldName, $context = NULL, $props = []) {
16c0ec8d 3050 $className = __CLASS__;
fc9f05e0 3051 $params = [];
16c0ec8d
CW
3052 switch ($fieldName) {
3053 // This field is not part of this object but the api supports it
3054 case 'medium_id':
3055 $className = 'CRM_Activity_BAO_Activity';
3056 break;
31c28ed5
CW
3057
3058 // Filter status id by case type id
3059 case 'status_id':
3060 if (!empty($props['case_type_id'])) {
3061 $idField = is_numeric($props['case_type_id']) ? 'id' : 'name';
fc9f05e0 3062 $caseType = civicrm_api3('CaseType', 'getsingle', [$idField => $props['case_type_id'], 'return' => 'definition']);
31c28ed5
CW
3063 if (!empty($caseType['definition']['statuses'])) {
3064 $params['condition'] = 'v.name IN ("' . implode('","', $caseType['definition']['statuses']) . '")';
3065 }
3066 }
3067 break;
16c0ec8d
CW
3068 }
3069 return CRM_Core_PseudoConstant::get($className, $fieldName, $params, $context);
3070 }
96025800 3071
174a1918
CW
3072 /**
3073 * @inheritDoc
3074 */
20e41014 3075 public function addSelectWhereClause() {
0b80f0b4
CW
3076 // We always return an array with these keys, even if they are empty,
3077 // because this tells the query builder that we have considered these fields for acls
fc9f05e0 3078 $clauses = [
3079 'id' => [],
ff9340a4 3080 // Only case admins can view deleted cases
fc9f05e0 3081 'is_deleted' => CRM_Core_Permission::check('administer CiviCase') ? [] : ["= 0"],
3082 ];
174a1918 3083 // Ensure the user has permission to view the case client
d1d3c04a 3084 $contactClause = CRM_Utils_SQL::mergeSubquery('Contact');
ff9340a4
CW
3085 if ($contactClause) {
3086 $contactClause = implode(' AND contact_id ', $contactClause);
3087 $clauses['id'][] = "IN (SELECT case_id FROM civicrm_case_contact WHERE contact_id $contactClause)";
174a1918 3088 }
0b80f0b4 3089 // The api gatekeeper ensures the user has at least "access my cases and activities"
174a1918
CW
3090 // so if they do not have permission to see all cases we'll assume they can only access their own
3091 if (!CRM_Core_Permission::check('access all cases and activities')) {
3092 $user = (int) CRM_Core_Session::getLoggedInContactID();
ff9340a4 3093 $clauses['id'][] = "IN (
174a1918 3094 SELECT r.case_id FROM civicrm_relationship r, civicrm_case_contact cc WHERE r.is_active = 1 AND cc.case_id = r.case_id AND (
ff9340a4 3095 (r.contact_id_a = cc.contact_id AND r.contact_id_b = $user) OR (r.contact_id_b = cc.contact_id AND r.contact_id_a = $user)
174a1918
CW
3096 )
3097 )";
3098 }
2b240c0c 3099 CRM_Utils_Hook::selectWhereClause($this, $clauses);
ff9340a4 3100 return $clauses;
174a1918
CW
3101 }
3102
8aeeea00 3103 /**
3cf1fae9 3104 * CRM-20308: Method to get the contact id to use as from contact for email copy
8aeeea00
EH
3105 * 1. Activity Added by Contact's email address
3106 * 2. System Default From Address
3107 * 3. Default Organization Contact email address
3108 * 4. Logged in user
3109 *
3cf1fae9 3110 * @param int $activityID
3111 *
8aeeea00 3112 * @return mixed $emailFromContactId
fc9f05e0 3113 *
3114 * @throws \CiviCRM_API3_Exception
8aeeea00
EH
3115 * @see https://issues.civicrm.org/jira/browse/CRM-20308
3116 */
3cf1fae9 3117 public static function getReceiptFrom($activityID) {
3118 $name = $address = NULL;
3119
4c981f37
MW
3120 if (!empty($activityID) && (Civi::settings()->get('allow_mail_from_logged_in_contact'))) {
3121 // This breaks SPF/DMARC if email is sent from an email address that the server is not authorised to send from.
3122 // so we can disable this behaviour with the "allow_mail_from_logged_in_contact" setting.
3cf1fae9 3123 // There is always a 'Added by' contact for a activity,
3124 // so we can safely use ActivityContact.Getvalue API
fc9f05e0 3125 $sourceContactId = civicrm_api3('ActivityContact', 'getvalue', [
3cf1fae9 3126 'activity_id' => $activityID,
3127 'record_type_id' => 'Activity Source',
3128 'return' => 'contact_id',
fc9f05e0 3129 ]);
3cf1fae9 3130 list($name, $address) = CRM_Contact_BAO_Contact_Location::getEmailDetails($sourceContactId);
8aeeea00 3131 }
3cf1fae9 3132
3133 // If 'From' email address not found for Source Activity Contact then
3134 // fetch the email from domain or logged in user.
3135 if (empty($address)) {
3136 list($name, $address) = CRM_Core_BAO_Domain::getDefaultReceiptFrom();
8aeeea00 3137 }
3cf1fae9 3138
8aeeea00
EH
3139 return "$name <$address>";
3140 }
3141
1d6f94ab
CW
3142 /**
3143 * @return array
3144 */
3145 public static function getEntityRefFilters() {
3146 $filters = [
3147 [
3148 'key' => 'case_id.case_type_id',
3149 'value' => ts('Case Type'),
3150 'entity' => 'Case',
3151 ],
3152 [
3153 'key' => 'case_id.status_id',
3154 'value' => ts('Case Status'),
3155 'entity' => 'Case',
3156 ],
3157 ];
3158 foreach (CRM_Contact_BAO_Contact::getEntityRefFilters() as $filter) {
2229cf4f 3159 $filter += ['entity' => 'Contact'];
1d6f94ab
CW
3160 $filter['key'] = 'contact_id.' . $filter['key'];
3161 $filters[] = $filter;
3162 }
3163 return $filters;
3164 }
3165
41cf58d3
AF
3166 /**
3167 * Fetch Case Role direction from Case Type
3168 */
3169 public static function getCaseRoleDirection($caseId, $roleTypeId = NULL) {
3170 try {
fc9f05e0 3171 $case = civicrm_api3('Case', 'getsingle', ['id' => $caseId]);
41cf58d3
AF
3172 }
3173 catch (CiviCRM_API3_Exception $e) {
3174 // Lack of permissions will throw an exception
3175 return 0;
3176 }
3177 if (!empty($case['case_type_id'])) {
3178 try {
fc9f05e0 3179 $caseType = civicrm_api3('CaseType', 'getsingle', ['id' => $case['case_type_id'], 'return' => ['definition']]);
41cf58d3
AF
3180 }
3181 catch (CiviCRM_API3_Exception $e) {
3182 // Lack of permissions will throw an exception
3183 return 'no case type found';
3184 }
3185 if (!empty($caseType['definition']['caseRoles'])) {
fc9f05e0 3186 $caseRoles = [];
41cf58d3
AF
3187 foreach ($caseType['definition']['caseRoles'] as $key => $roleDetails) {
3188 // Check if its an a_b label
3189 try {
fc9f05e0 3190 $relType = civicrm_api3('RelationshipType', 'getsingle', ['label_a_b' => $roleDetails['name']]);
41cf58d3
AF
3191 }
3192 catch (CiviCRM_API3_Exception $e) {
3193 }
3194 if (!empty($relType['id'])) {
3195 $roleDetails['id'] = $relType['id'];
d0a94888 3196 $roleDetails['direction'] = 'b_a';
41cf58d3
AF
3197 }
3198 // Check if its a b_a label
3199 try {
fc9f05e0 3200 $relTypeBa = civicrm_api3('RelationshipType', 'getsingle', ['label_b_a' => $roleDetails['name']]);
41cf58d3
AF
3201 }
3202 catch (CiviCRM_API3_Exception $e) {
3203 }
3204 if (!empty($relTypeBa['id'])) {
3205 if (!empty($roleDetails['direction'])) {
3206 $roleDetails['direction'] = 'bidrectional';
3207 }
3208 else {
3209 $roleDetails['id'] = $relTypeBa['id'];
d0a94888 3210 $roleDetails['direction'] = 'a_b';
41cf58d3
AF
3211 }
3212 }
3213 $caseRoles[$roleDetails['id']] = $roleDetails;
3214 }
3215 }
3216 return $caseRoles;
3217 }
3218 }
3219
6a488035 3220}