return manager info during api call
[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 1143 $query = <<<HERESQL
c50e15c8 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, crt.name_a_b as role_name_reverse, ce.email, cp.phone
41cf58d3
AF
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
c50e15c8 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, crt.name_b_a as role_name_reverse, ce.email, cp.phone
41cf58d3
AF
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)
c50e15c8 1199 // The XML historically has the reverse direction, so look up reverse.
1200 $role = CRM_Utils_Array::value($dao->role_name_reverse, $caseRoles);
69f9c562
CW
1201 if ($role) {
1202 unset($role['name']);
1203 $details += $role;
1204 }
1205 $values[] = $details;
6a488035
TO
1206 }
1207 }
6a488035
TO
1208
1209 return $values;
1210 }
1211
1212 /**
100fef9d 1213 * Send e-mail copy of activity
6a488035 1214 *
100fef9d 1215 * @param int $clientId
64bd5a0e
TO
1216 * @param int $activityId
1217 * Activity Id.
1218 * @param array $contacts
1219 * Array of related contact.
6a488035 1220 *
77b97be7 1221 * @param null $attachments
100fef9d 1222 * @param int $caseId
77b97be7 1223 *
67d19299 1224 * @return bool |array
6a488035 1225 */
00be9182 1226 public static function sendActivityCopy($clientId, $activityId, $contacts, $attachments = NULL, $caseId) {
6a488035 1227 if (!$activityId) {
67d19299 1228 return FALSE;
6a488035
TO
1229 }
1230
fc9f05e0 1231 $tplParams = $activityInfo = [];
8d04ae48
AP
1232 $activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id');
1233 // If it's a case activity
6a488035 1234 if ($caseId) {
6a488035 1235 $nonCaseActivityTypes = CRM_Core_PseudoConstant::activityType();
a7488080 1236 if (!empty($nonCaseActivityTypes[$activityTypeId])) {
6a488035
TO
1237 $anyActivity = TRUE;
1238 }
1239 else {
1240 $anyActivity = FALSE;
1241 }
1242 $tplParams['isCaseActivity'] = 1;
1243 $tplParams['client_id'] = $clientId;
1244 }
1245 else {
1246 $anyActivity = TRUE;
1247 }
1248
1249 $xmlProcessorProcess = new CRM_Case_XMLProcessor_Process();
1250 $isRedact = $xmlProcessorProcess->getRedactActivityEmail();
1251
1252 $xmlProcessorReport = new CRM_Case_XMLProcessor_Report();
1253
1254 $activityInfo = $xmlProcessorReport->getActivityInfo($clientId, $activityId, $anyActivity, $isRedact);
1255 if ($caseId) {
fc9f05e0 1256 $activityInfo['fields'][] = ['label' => 'Case ID', 'type' => 'String', 'value' => $caseId];
6a488035 1257 }
8d04ae48 1258 $tplParams['activityTypeName'] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_DAO_Activity', 'activity_type_id', $activityTypeId);
6a488035
TO
1259 $tplParams['activity'] = $activityInfo;
1260 foreach ($tplParams['activity']['fields'] as $k => $val) {
1261 if (CRM_Utils_Array::value('label', $val) == ts('Subject')) {
1262 $activitySubject = $val['value'];
1263 break;
1264 }
1265 }
1266 $session = CRM_Core_Session::singleton();
1267 // CRM-8926 If user is not logged in, use the activity creator as userID
1268 if (!($userID = $session->get('userID'))) {
4322672b 1269 $userID = CRM_Activity_BAO_Activity::getSourceContactID($activityId);
6a488035
TO
1270 }
1271
1272 //also create activities simultaneously of this copy.
fc9f05e0 1273 $activityParams = [];
6a488035
TO
1274
1275 $activityParams['source_record_id'] = $activityId;
1276 $activityParams['source_contact_id'] = $userID;
95a718e1 1277 $activityParams['activity_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_type_id', 'Email');
6a488035 1278 $activityParams['activity_date_time'] = date('YmdHis');
95a718e1
JP
1279 $activityParams['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'activity_status_id', 'Completed');
1280 $activityParams['medium_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_DAO_Activity', 'encounter_medium', 'email');
6a488035
TO
1281 $activityParams['case_id'] = $caseId;
1282 $activityParams['is_auto'] = 0;
1283 $activityParams['target_id'] = $clientId;
1284
1285 $tplParams['activitySubject'] = $activitySubject;
1286
1287 // if it’s a case activity, add hashed id to the template (CRM-5916)
1288 if ($caseId) {
1289 $tplParams['idHash'] = substr(sha1(CIVICRM_SITE_KEY . $caseId), 0, 7);
1290 }
1291
fc9f05e0 1292 $result = [];
8aeeea00
EH
1293 // CRM-20308 get receiptFrom defaults see https://issues.civicrm.org/jira/browse/CRM-20308
1294 $receiptFrom = self::getReceiptFrom($activityId);
6a488035 1295
fc9f05e0 1296 $recordedActivityParams = [];
6a488035
TO
1297
1298 foreach ($contacts as $mail => $info) {
1299 $tplParams['contact'] = $info;
1300 self::buildPermissionLinks($tplParams, $activityParams);
1301
21827a41 1302 $displayName = CRM_Utils_Array::value('display_name', $info);
6a488035 1303
c6327d7d 1304 list($result[CRM_Utils_Array::value('contact_id', $info)], $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate(
fc9f05e0 1305 [
6a488035
TO
1306 'groupName' => 'msg_tpl_workflow_case',
1307 'valueName' => 'case_activity',
21827a41 1308 'contactId' => CRM_Utils_Array::value('contact_id', $info),
6a488035
TO
1309 'tplParams' => $tplParams,
1310 'from' => $receiptFrom,
1311 'toName' => $displayName,
1312 'toEmail' => $mail,
1313 'attachments' => $attachments,
fc9f05e0 1314 ]
6a488035
TO
1315 );
1316
898f01ea 1317 $activityParams['subject'] = ts('%1 - copy sent to %2', [1 => $activitySubject, 2 => $displayName]);
6a488035
TO
1318 $activityParams['details'] = $message;
1319
21827a41 1320 if (!empty($result[$info['contact_id']])) {
6a488035
TO
1321 /*
1322 * Really only need to record one activity with all the targets combined.
1323 * Originally the template was going to possibly have different content, e.g. depending on permissions,
1324 * but it's always the same content at the moment.
1325 */
1326 if (empty($recordedActivityParams)) {
1327 $recordedActivityParams = $activityParams;
1328 }
1329 else {
1330 $recordedActivityParams['subject'] .= "; $displayName";
1331 }
1332 $recordedActivityParams['target_contact_id'][] = $info['contact_id'];
1333 }
1334 else {
21827a41 1335 unset($result[CRM_Utils_Array::value('contact_id', $info)]);
6a488035
TO
1336 }
1337 }
1338
1339 if (!empty($recordedActivityParams)) {
1340 $activity = CRM_Activity_BAO_Activity::create($recordedActivityParams);
1341
1342 //create case_activity record if its case activity.
1343 if ($caseId) {
fc9f05e0 1344 $caseParams = [
6a488035
TO
1345 'activity_id' => $activity->id,
1346 'case_id' => $caseId,
fc9f05e0 1347 ];
6a488035
TO
1348 self::processCaseActivity($caseParams);
1349 }
1350 }
1351
1352 return $result;
1353 }
1354
1355 /**
1356 * Retrieve count of activities having a particular type, and
1357 * associated with a particular case.
1358 *
64bd5a0e
TO
1359 * @param int $caseId
1360 * ID of the case.
1361 * @param int $activityTypeId
1362 * ID of the activity type.
6a488035
TO
1363 *
1364 * @return array
6a488035 1365 */
00be9182 1366 public static function getCaseActivityCount($caseId, $activityTypeId) {
fc9f05e0 1367 $queryParam = [
1368 1 => [$caseId, 'Integer'],
1369 2 => [$activityTypeId, 'Integer'],
1370 ];
6a488035
TO
1371 $query = "SELECT count(ca.id) as countact
1372 FROM civicrm_activity ca
1373 INNER JOIN civicrm_case_activity cca ON ca.id = cca.activity_id
1374 WHERE ca.activity_type_id = %2
1375 AND cca.case_id = %1
1376 AND ca.is_deleted = 0";
1377
1378 $dao = CRM_Core_DAO::executeQuery($query, $queryParam);
1379 if ($dao->fetch()) {
1380 return $dao->countact;
1381 }
1382
1383 return FALSE;
1384 }
1385
1386 /**
d2e5d2ce 1387 * Create an activity for a case via email.
6a488035 1388 *
64bd5a0e
TO
1389 * @param int $file
1390 * Email sent.
6a488035 1391 *
72b3a70c
CW
1392 * @return array|void
1393 * $activity object of newly creted activity via email
6a488035 1394 */
00be9182 1395 public static function recordActivityViaEmail($file) {
6a488035
TO
1396 if (!file_exists($file) ||
1397 !is_readable($file)
1398 ) {
1399 return CRM_Core_Error::fatal(ts('File %1 does not exist or is not readable',
fc9f05e0 1400 [1 => $file]
d6f468d3 1401 ));
6a488035
TO
1402 }
1403
1404 $result = CRM_Utils_Mail_Incoming::parse($file);
1405 if ($result['is_error']) {
1406 return $result;
1407 }
1408
1409 foreach ($result['to'] as $to) {
1410 $caseId = NULL;
1411
1412 $emailPattern = '/^([A-Z0-9._%+-]+)\+([\d]+)@[A-Z0-9.-]+\.[A-Z]{2,4}$/i';
1413 $replacement = preg_replace($emailPattern, '$2', $to['email']);
1414
1415 if ($replacement !== $to['email']) {
1416 $caseId = $replacement;
1417 //if caseId is invalid, return as error file
1418 if (!CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseId, 'id')) {
1419 return CRM_Core_Error::createAPIError(ts('Invalid case ID ( %1 ) in TO: field.',
fc9f05e0 1420 [1 => $caseId]
d6f468d3 1421 ));
6a488035
TO
1422 }
1423 }
1424 else {
1425 continue;
1426 }
1427
1428 // TODO: May want to replace this with a call to getRelatedAndGlobalContacts() when this feature is revisited.
1429 // (Or for efficiency call the global one outside the loop and then union with this each time.)
b982dca0 1430 $contactDetails = self::getRelatedContacts($caseId, FALSE);
6a488035 1431
a7488080 1432 if (!empty($contactDetails[$result['from']['id']])) {
fc9f05e0 1433 $params = [];
6a488035
TO
1434 $params['subject'] = $result['subject'];
1435 $params['activity_date_time'] = $result['date'];
1436 $params['details'] = $result['body'];
1437 $params['source_contact_id'] = $result['from']['id'];
9c248a42 1438 $params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed');
6a488035
TO
1439
1440 $details = CRM_Case_PseudoConstant::caseActivityType();
fc9f05e0 1441 $matches = [];
6a488035
TO
1442 preg_match('/^\W+([a-zA-Z0-9_ ]+)(\W+)?\n/i',
1443 $result['body'], $matches
1444 );
1445
1446 if (!empty($matches) && isset($matches[1])) {
1447 $activityType = trim($matches[1]);
1448 if (isset($details[$activityType])) {
1449 $params['activity_type_id'] = $details[$activityType]['id'];
1450 }
1451 }
1452 if (!isset($params['activity_type_id'])) {
9c248a42 1453 $params['activity_type_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Inbound Email');
6a488035
TO
1454 }
1455
1456 // create activity
1457 $activity = CRM_Activity_BAO_Activity::create($params);
1458
fc9f05e0 1459 $caseParams = [
6a488035
TO
1460 'activity_id' => $activity->id,
1461 'case_id' => $caseId,
fc9f05e0 1462 ];
6a488035
TO
1463 self::processCaseActivity($caseParams);
1464 }
1465 else {
1466 return CRM_Core_Error::createAPIError(ts('FROM email contact %1 doesn\'t have a relationship to the referenced case.',
fc9f05e0 1467 [1 => $result['from']['email']]
d6f468d3 1468 ));
6a488035
TO
1469 }
1470 }
1471 }
1472
1473 /**
d2e5d2ce 1474 * Retrieve the scheduled activity type and date.
6a488035 1475 *
64bd5a0e
TO
1476 * @param array $cases
1477 * Array of contact and case id.
77b97be7
EM
1478 *
1479 * @param string $type
6a488035 1480 *
a6c01b45
CW
1481 * @return array
1482 * Array of scheduled activity type and date
6a488035 1483 *
6a488035 1484 *
6a488035 1485 */
00be9182 1486 public static function getNextScheduledActivity($cases, $type = 'upcoming') {
6a488035
TO
1487 $session = CRM_Core_Session::singleton();
1488 $userID = $session->get('userID');
1489
1490 $caseID = implode(',', $cases['case_id']);
1491 $contactID = implode(',', $cases['contact_id']);
1492
5f1c8c57 1493 $condition = " civicrm_case_contact.contact_id IN( {$contactID} )
6a488035
TO
1494 AND civicrm_case.id IN( {$caseID})
1495 AND civicrm_case.is_deleted = {$cases['case_deleted']}";
1496
2ff620ae 1497 $query = self::getCaseActivityQuery($type, $userID, $condition);
57d38398 1498 $activityTypes = CRM_Activity_BAO_Activity::buildOptions('activity_type_id');
6a488035 1499
33621c4f 1500 $res = CRM_Core_DAO::executeQuery($query);
6a488035 1501
fc9f05e0 1502 $activityInfo = [];
6a488035
TO
1503 while ($res->fetch()) {
1504 if ($type == 'upcoming') {
2c783cce 1505 $activityInfo[$res->case_id]['date'] = $res->activity_date_time;
57d38398 1506 $activityInfo[$res->case_id]['type'] = CRM_Utils_Array::value($res->activity_type_id, $activityTypes);
6a488035
TO
1507 }
1508 else {
2c783cce 1509 $activityInfo[$res->case_id]['date'] = $res->activity_date_time;
57d38398 1510 $activityInfo[$res->case_id]['type'] = CRM_Utils_Array::value($res->activity_type_id, $activityTypes);
6a488035
TO
1511 }
1512 }
1513
1514 return $activityInfo;
1515 }
1516
1517 /**
d2e5d2ce 1518 * Combine all the exportable fields from the lower levels object.
6a488035 1519 *
a6c01b45
CW
1520 * @return array
1521 * array of exportable Fields
6a488035 1522 */
00be9182 1523 public static function &exportableFields() {
6a488035
TO
1524 if (!self::$_exportableFields) {
1525 if (!self::$_exportableFields) {
fc9f05e0 1526 self::$_exportableFields = [];
6a488035
TO
1527 }
1528
e96f025a 1529 $fields = CRM_Case_DAO_Case::export();
fc9f05e0 1530 $fields['case_role'] = ['title' => ts('Role in Case')];
1531 $fields['case_type'] = [
e96f025a 1532 'title' => ts('Case Type'),
6a488035 1533 'name' => 'case_type',
fc9f05e0 1534 ];
1535 $fields['case_status'] = [
e96f025a 1536 'title' => ts('Case Status'),
6a488035 1537 'name' => 'case_status',
fc9f05e0 1538 ];
6a488035 1539
e75182f2
JJ
1540 // add custom data for cases
1541 $fields = array_merge($fields, CRM_Core_BAO_CustomField::getFieldsForImport('Case'));
1542
6a488035
TO
1543 self::$_exportableFields = $fields;
1544 }
1545 return self::$_exportableFields;
1546 }
1547
1548 /**
d2e5d2ce 1549 * Restore the record that are associated with this case.
6a488035 1550 *
64bd5a0e
TO
1551 * @param int $caseId
1552 * Id of the case to restore.
6a488035 1553 *
72b3a70c 1554 * @return bool
6a488035 1555 */
00be9182 1556 public static function restoreCase($caseId) {
6a488035
TO
1557 //restore activities
1558 $activities = self::getCaseActivityDates($caseId);
1559 if ($activities) {
1560 foreach ($activities as $value) {
1561 CRM_Activity_BAO_Activity::restoreActivity($value);
1562 }
1563 }
1564 //restore case
e96f025a 1565 $case = new CRM_Case_DAO_Case();
1566 $case->id = $caseId;
6a488035
TO
1567 $case->is_deleted = 0;
1568 $case->save();
1569
1570 //CRM-7364, enable relationships
1571 self::enableDisableCaseRelationships($caseId, TRUE);
1572 return TRUE;
1573 }
1574
4c6ce474
EM
1575 /**
1576 * @param $groupInfo
1577 * @param null $sort
1578 * @param null $showLinks
1579 * @param bool $returnOnlyCount
1580 * @param int $offset
1581 * @param int $rowCount
1582 *
1583 * @return array
1584 */
00be9182 1585 public static function getGlobalContacts(&$groupInfo, $sort = NULL, $showLinks = NULL, $returnOnlyCount = FALSE, $offset = 0, $rowCount = 25) {
fc9f05e0 1586 $globalContacts = [];
6a488035
TO
1587
1588 $settingsProcessor = new CRM_Case_XMLProcessor_Settings();
1589 $settings = $settingsProcessor->run();
1590 if (!empty($settings)) {
1591 $groupInfo['name'] = $settings['groupname'];
1592 if ($groupInfo['name']) {
fc9f05e0 1593 $searchParams = ['name' => $groupInfo['name']];
1594 $results = [];
6a488035
TO
1595 CRM_Contact_BAO_Group::retrieve($searchParams, $results);
1596 if ($results) {
e96f025a 1597 $groupInfo['id'] = $results['id'];
6a488035 1598 $groupInfo['title'] = $results['title'];
fc9f05e0 1599 $params = [['group', '=', $groupInfo['id'], 0, 0]];
1600 $return = ['contact_id' => 1, 'sort_name' => 1, 'display_name' => 1, 'email' => 1, 'phone' => 1];
d79c94d5 1601 list($globalContacts) = CRM_Contact_BAO_Query::apiQuery($params, $return, NULL, $sort, $offset, $rowCount, TRUE, $returnOnlyCount);
6a488035
TO
1602
1603 if ($returnOnlyCount) {
1604 return $globalContacts;
1605 }
1606
1607 if ($showLinks) {
e96f025a 1608 foreach ($globalContacts as $idx => $contact) {
d79c94d5 1609 $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
1610 }
1611 }
1612 }
1613 }
1614 }
1615 return $globalContacts;
1616 }
1617
4c6ce474 1618 /**
d2e5d2ce 1619 * Convenience function to get both case contacts and global in one array.
fc9f05e0 1620 *
100fef9d 1621 * @param int $caseId
4c6ce474
EM
1622 *
1623 * @return array
1624 */
00be9182 1625 public static function getRelatedAndGlobalContacts($caseId) {
6a488035
TO
1626 $relatedContacts = self::getRelatedContacts($caseId);
1627
fc9f05e0 1628 $groupInfo = [];
6a488035
TO
1629 $globalContacts = self::getGlobalContacts($groupInfo);
1630
1631 //unset values which are not required.
1632 foreach ($globalContacts as $k => & $v) {
1633 unset($v['email_id']);
1634 unset($v['group_contact_id']);
1635 unset($v['status']);
1636 unset($v['phone']);
1637 $v['role'] = $groupInfo['title'];
1638 }
1639 //include multiple listings for the same contact/different roles.
1640 $relatedGlobalContacts = array_merge($relatedContacts, $globalContacts);
1641 return $relatedGlobalContacts;
1642 }
1643
1644 /**
100fef9d 1645 * Get Case ActivitiesDueDates with given criteria.
6a488035 1646 *
64bd5a0e
TO
1647 * @param int $caseID
1648 * Case id.
1649 * @param array $criteriaParams
1650 * Given criteria.
1651 * @param bool $latestDate
72b3a70c 1652 * If set newest or oldest date is selected.
6a488035 1653 *
72b3a70c
CW
1654 * @return array
1655 * case activities due dates
6a488035 1656 *
6a488035 1657 */
fc9f05e0 1658 public static function getCaseActivityDates($caseID, $criteriaParams = [], $latestDate = FALSE) {
1659 $values = [];
6a488035 1660 $selectDate = " ca.activity_date_time";
e96f025a 1661 $where = $groupBy = ' ';
6a488035
TO
1662
1663 if (!$caseID) {
408b79bf 1664 return NULL;
6a488035
TO
1665 }
1666
1667 if ($latestDate) {
a7488080 1668 if (!empty($criteriaParams['activity_type_id'])) {
6a488035
TO
1669 $where .= " AND ca.activity_type_id = " . CRM_Utils_Type::escape($criteriaParams['activity_type_id'], 'Integer');
1670 $where .= " AND ca.is_current_revision = 1";
e5cceea5 1671 $groupBy .= " GROUP BY ca.activity_type_id, ca.id";
6a488035
TO
1672 }
1673
a7488080 1674 if (!empty($criteriaParams['newest'])) {
6a488035
TO
1675 $selectDate = " max(ca.activity_date_time) ";
1676 }
1677 else {
1678 $selectDate = " min(ca.activity_date_time) ";
1679 }
1680 }
1681
1682 $query = "SELECT ca.id, {$selectDate} as activity_date
1683 FROM civicrm_activity ca
1684 LEFT JOIN civicrm_case_activity cca ON cca.activity_id = ca.id LEFT JOIN civicrm_case cc ON cc.id = cca.case_id
1685 WHERE cc.id = %1 {$where} {$groupBy}";
1686
fc9f05e0 1687 $params = [1 => [$caseID, 'Integer']];
6a488035
TO
1688 $dao = CRM_Core_DAO::executeQuery($query, $params);
1689
1690 while ($dao->fetch()) {
1691 $values[$dao->id]['id'] = $dao->id;
1692 $values[$dao->id]['activity_date'] = $dao->activity_date;
1693 }
6a488035
TO
1694 return $values;
1695 }
1696
1697 /**
100fef9d 1698 * Create activities when Case or Other roles assigned/modified/deleted.
6a488035 1699 *
100fef9d 1700 * @param int $caseId
64bd5a0e
TO
1701 * @param int $relationshipId
1702 * Relationship id.
1703 * @param int $relContactId
1704 * Case role assignee contactId.
100fef9d 1705 * @param int $contactId
6a488035 1706 */
00be9182 1707 public static function createCaseRoleActivity($caseId, $relationshipId, $relContactId = NULL, $contactId = NULL) {
6a488035
TO
1708 if (!$caseId || !$relationshipId || empty($relationshipId)) {
1709 return;
1710 }
1711
fc9f05e0 1712 $queryParam = [];
6a488035
TO
1713 if (is_array($relationshipId)) {
1714 $relationshipId = implode(',', $relationshipId);
1715 $relationshipClause = " civicrm_relationship.id IN ($relationshipId)";
1716 }
1717 else {
1718 $relationshipClause = " civicrm_relationship.id = %1";
fc9f05e0 1719 $queryParam[1] = [$relationshipId, 'Positive'];
6a488035
TO
1720 }
1721
1722 $query = "
1723 SELECT cc.display_name as clientName,
1724 cca.display_name as assigneeContactName,
1725 civicrm_relationship.case_id as caseId,
1726 civicrm_relationship_type.label_a_b as relation_a_b,
1727 civicrm_relationship_type.label_b_a as relation_b_a,
1728 civicrm_relationship.contact_id_b as rel_contact_id,
1729 civicrm_relationship.contact_id_a as assign_contact_id
1730 FROM civicrm_relationship_type, civicrm_relationship
1731 LEFT JOIN civicrm_contact cc ON cc.id = civicrm_relationship.contact_id_b
1732 LEFT JOIN civicrm_contact cca ON cca.id = civicrm_relationship.contact_id_a
1733 WHERE civicrm_relationship.relationship_type_id = civicrm_relationship_type.id AND {$relationshipClause}";
1734
1735 $dao = CRM_Core_DAO::executeQuery($query, $queryParam);
1736
1737 while ($dao->fetch()) {
e3b9b4f6
KW
1738 // The assignee is not the client.
1739 if ($dao->rel_contact_id != $contactId) {
6a488035
TO
1740 $caseRelationship = $dao->relation_a_b;
1741 $assigneContactName = $dao->clientName;
1742 $assigneContactIds[$dao->rel_contact_id] = $dao->rel_contact_id;
1743 }
1744 else {
1745 $caseRelationship = $dao->relation_b_a;
1746 $assigneContactName = $dao->assigneeContactName;
1747 $assigneContactIds[$dao->assign_contact_id] = $dao->assign_contact_id;
1748 }
1749 }
1750
1751 $session = CRM_Core_Session::singleton();
fc9f05e0 1752 $activityParams = [
6a488035
TO
1753 'source_contact_id' => $session->get('userID'),
1754 'subject' => $caseRelationship . ' : ' . $assigneContactName,
1755 'activity_date_time' => date('YmdHis'),
9c248a42 1756 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'),
fc9f05e0 1757 ];
6a488035
TO
1758
1759 //if $relContactId is passed, role is added or modified.
1760 if (!empty($relContactId)) {
1761 $activityParams['assignee_contact_id'] = $assigneContactIds;
9c248a42 1762 $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Assign Case Role');
6a488035
TO
1763 }
1764 else {
9c248a42 1765 $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Remove Case Role');
6a488035
TO
1766 }
1767
1768 $activityParams['activity_type_id'] = $activityTypeID;
1769
1770 $activity = CRM_Activity_BAO_Activity::create($activityParams);
1771
1772 //create case_activity record.
fc9f05e0 1773 $caseParams = [
6a488035
TO
1774 'activity_id' => $activity->id,
1775 'case_id' => $caseId,
fc9f05e0 1776 ];
6a488035
TO
1777
1778 CRM_Case_BAO_Case::processCaseActivity($caseParams);
1779 }
1780
1781 /**
100fef9d 1782 * Get case manger
6a488035
TO
1783 * contact which is assigned a case role of case manager.
1784 *
64bd5a0e
TO
1785 * @param int $caseType
1786 * Case type.
1787 * @param int $caseId
1788 * Case id.
6a488035 1789 *
c00bd201 1790 * @return string
1791 * html hyperlink of manager contact view page
6a488035 1792 *
6a488035 1793 */
00be9182 1794 public static function getCaseManagerContact($caseType, $caseId) {
6a488035 1795 if (!$caseType || !$caseId) {
408b79bf 1796 return NULL;
6a488035
TO
1797 }
1798
5757b086 1799 $caseManagerName = '---';
6a488035
TO
1800 $xmlProcessor = new CRM_Case_XMLProcessor_Process();
1801
1802 $managerRoleId = $xmlProcessor->getCaseManagerRoleId($caseType);
1803
1804 if (!empty($managerRoleId)) {
41cf58d3
AF
1805 if (substr($managerRoleId, -4) == '_a_b') {
1806 $managerRoleQuery = "
1807 SELECT civicrm_contact.id as casemanager_id,
1808 civicrm_contact.sort_name as casemanager
1809 FROM civicrm_contact
1810 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
1811 LEFT JOIN civicrm_case ON civicrm_case.id = civicrm_relationship.case_id
1812 WHERE civicrm_case.id = %2 AND is_active = 1";
1813 }
1814 if (substr($managerRoleId, -4) == '_b_a') {
1815 $managerRoleQuery = "
1816 SELECT civicrm_contact.id as casemanager_id,
1817 civicrm_contact.sort_name as casemanager
1818 FROM civicrm_contact
1819 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
1820 LEFT JOIN civicrm_case ON civicrm_case.id = civicrm_relationship.case_id
1821 WHERE civicrm_case.id = %2 AND is_active = 1";
1822 }
6a488035 1823
fc9f05e0 1824 $managerRoleParams = [
1825 1 => [substr($managerRoleId, 0, -4), 'Integer'],
1826 2 => [$caseId, 'Integer'],
1827 ];
6a488035
TO
1828
1829 $dao = CRM_Core_DAO::executeQuery($managerRoleQuery, $managerRoleParams);
1830 if ($dao->fetch()) {
5757b086 1831 $caseManagerName = sprintf('<a href="%s">%s</a>',
fc9f05e0 1832 CRM_Utils_System::url('civicrm/contact/view', ['cid' => $dao->casemanager_id]),
c00bd201 1833 $dao->casemanager
1834 );
6a488035
TO
1835 }
1836 }
5757b086 1837
1838 return $caseManagerName;
6a488035
TO
1839 }
1840
4c6ce474 1841 /**
100fef9d 1842 * @param int $contactId
4c6ce474
EM
1843 * @param bool $excludeDeleted
1844 *
eeb45e43 1845 * @return int
4c6ce474 1846 */
00be9182 1847 public static function caseCount($contactId = NULL, $excludeDeleted = TRUE) {
fc9f05e0 1848 $params = ['check_permissions' => TRUE];
6a488035 1849 if ($excludeDeleted) {
0a1a8b63 1850 $params['is_deleted'] = 0;
6a488035
TO
1851 }
1852 if ($contactId) {
0a1a8b63 1853 $params['contact_id'] = $contactId;
6a488035 1854 }
eeb45e43
CW
1855 try {
1856 return civicrm_api3('Case', 'getcount', $params);
1857 }
1858 catch (CiviCRM_API3_Exception $e) {
1859 // Lack of permissions will throw an exception
1860 return 0;
1861 }
6a488035
TO
1862 }
1863
1864 /**
6e19e2ea 1865 * Retrieve related case ids for given case.
6a488035 1866 *
6e19e2ea 1867 * @param int $caseId
64bd5a0e
TO
1868 * @param bool $excludeDeleted
1869 * Do not include deleted cases.
6a488035 1870 *
72b3a70c 1871 * @return array
6a488035 1872 */
6e19e2ea 1873 public static function getRelatedCaseIds($caseId, $excludeDeleted = TRUE) {
6a488035
TO
1874 //FIXME : do check for permissions.
1875
6e19e2ea 1876 if (!$caseId) {
fc9f05e0 1877 return [];
6a488035
TO
1878 }
1879
b864360d 1880 $linkActType = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Link Cases');
6a488035 1881 if (!$linkActType) {
fc9f05e0 1882 return [];
6a488035
TO
1883 }
1884
1885 $whereClause = "mainCase.id = %2";
1886 if ($excludeDeleted) {
1887 $whereClause .= " AND ( relAct.is_deleted = 0 OR relAct.is_deleted IS NULL )";
1888 }
1889
6a488035
TO
1890 $query = "
1891 SELECT relCaseAct.case_id
1892 FROM civicrm_case mainCase
1893 INNER JOIN civicrm_case_activity mainCaseAct ON (mainCaseAct.case_id = mainCase.id)
1894 INNER JOIN civicrm_activity mainAct ON (mainCaseAct.activity_id = mainAct.id AND mainAct.activity_type_id = %1)
1895 INNER JOIN civicrm_case_activity relCaseAct ON (relCaseAct.activity_id = mainAct.id AND mainCaseAct.id != relCaseAct.id)
1896 INNER JOIN civicrm_activity relAct ON (relCaseAct.activity_id = relAct.id AND relAct.activity_type_id = %1)
1897 WHERE $whereClause";
1898
fc9f05e0 1899 $dao = CRM_Core_DAO::executeQuery($query, [
1900 1 => [$linkActType, 'Integer'],
1901 2 => [$caseId, 'Integer'],
1902 ]);
1903 $relatedCaseIds = [];
6a488035
TO
1904 while ($dao->fetch()) {
1905 $relatedCaseIds[$dao->case_id] = $dao->case_id;
1906 }
6a488035 1907
6e19e2ea
CW
1908 return array_values($relatedCaseIds);
1909 }
1910
1911 /**
1912 * Retrieve related case details for given case.
1913 *
1914 * @param int $caseId
1915 * @param bool $excludeDeleted
1916 * Do not include deleted cases.
1917 *
1918 * @return array
1919 */
1920 public static function getRelatedCases($caseId, $excludeDeleted = TRUE) {
1921 $relatedCaseIds = self::getRelatedCaseIds($caseId, $excludeDeleted);
fc9f05e0 1922 $relatedCases = [];
6e19e2ea
CW
1923
1924 if (!$relatedCaseIds) {
fc9f05e0 1925 return [];
6a488035
TO
1926 }
1927
1928 $whereClause = 'relCase.id IN ( ' . implode(',', $relatedCaseIds) . ' )';
1929 if ($excludeDeleted) {
1930 $whereClause .= " AND ( relCase.is_deleted = 0 OR relCase.is_deleted IS NULL )";
1931 }
1932
1933 //filter for permissioned cases.
fc9f05e0 1934 $filterCases = [];
6a488035
TO
1935 $doFilterCases = FALSE;
1936 if (!CRM_Core_Permission::check('access all cases and activities')) {
1937 $doFilterCases = TRUE;
5f1c8c57 1938 $filterCases = CRM_Case_BAO_Case::getCases(FALSE);
6a488035
TO
1939 }
1940
1941 //2. fetch the details of related cases.
1942 $query = "
1943 SELECT relCase.id as id,
8ffdec17 1944 civicrm_case_type.title as case_type,
6a488035 1945 client.display_name as client_name,
74b15fae
CR
1946 client.id as client_id,
1947 relCase.status_id
6a488035
TO
1948 FROM civicrm_case relCase
1949 INNER JOIN civicrm_case_contact relCaseContact ON ( relCase.id = relCaseContact.case_id )
1950 INNER JOIN civicrm_contact client ON ( client.id = relCaseContact.contact_id )
8ffdec17 1951 LEFT JOIN civicrm_case_type ON relCase.case_type_id = civicrm_case_type.id
6a488035
TO
1952 WHERE {$whereClause}";
1953
e96f025a 1954 $dao = CRM_Core_DAO::executeQuery($query);
6a488035
TO
1955 $contactViewUrl = CRM_Utils_System::url("civicrm/contact/view", "reset=1&cid=");
1956 $hasViewContact = CRM_Core_Permission::giveMeAllACLs();
74b15fae 1957 $statuses = CRM_Case_BAO_Case::buildOptions('status_id');
6a488035
TO
1958
1959 while ($dao->fetch()) {
1960 $caseView = NULL;
1961 if (!$doFilterCases || array_key_exists($dao->id, $filterCases)) {
1962 $caseViewStr = "reset=1&id={$dao->id}&cid={$dao->client_id}&action=view&context=case&selectedChild=case";
1963 $caseViewUrl = CRM_Utils_System::url("civicrm/contact/view/case", $caseViewStr);
6ce08914 1964 $caseView = "<a class='action-item no-popup crm-hover-button' href='{$caseViewUrl}'>" . ts('View Case') . "</a>";
6a488035
TO
1965 }
1966 $clientView = $dao->client_name;
1967 if ($hasViewContact) {
1968 $clientView = "<a href='{$contactViewUrl}{$dao->client_id}'>$dao->client_name</a>";
1969 }
1970
fc9f05e0 1971 $relatedCases[$dao->id] = [
6a488035
TO
1972 'case_id' => $dao->id,
1973 'case_type' => $dao->case_type,
1974 'client_name' => $clientView,
74b15fae 1975 'case_status' => $statuses[$dao->status_id],
6a488035 1976 'links' => $caseView,
fc9f05e0 1977 ];
6a488035 1978 }
6a488035
TO
1979
1980 return $relatedCases;
1981 }
1982
1983 /**
1984 * Merge two duplicate contacts' cases - follow CRM-5758 rules.
1985 *
fc9f05e0 1986 * @param int $mainContactId
1987 * @param int $otherContactId
1988 *
6a488035
TO
1989 * @see CRM_Dedupe_Merger::cpTables()
1990 *
1991 * TODO: use the 3rd $sqls param to append sql statements rather than executing them here
cde2037d 1992 *
6a488035 1993 */
00be9182 1994 public static function mergeContacts($mainContactId, $otherContactId) {
6a488035
TO
1995 self::mergeCases($mainContactId, NULL, $otherContactId);
1996 }
1997
1998 /**
1999 * Function perform two task.
2000 * 1. Merge two duplicate contacts cases - follow CRM-5758 rules.
2001 * 2. Merge two cases of same contact - follow CRM-5598 rules.
2002 *
64bd5a0e
TO
2003 * @param int $mainContactId
2004 * Contact id of main contact record.
2005 * @param int $mainCaseId
2006 * Case id of main case record.
2007 * @param int $otherContactId
2008 * Contact id of record which is going to merge.
2009 * @param int $otherCaseId
2010 * Case id of record which is going to merge.
77b97be7
EM
2011 *
2012 * @param bool $changeClient
6a488035 2013 *
e97c66ff 2014 * @return int|null
2015 * @throws \CRM_Core_Exception
6a488035 2016 */
a130e045 2017 public static function mergeCases(
28d4d481
TO
2018 $mainContactId, $mainCaseId = NULL, $otherContactId = NULL,
2019 $otherCaseId = NULL, $changeClient = FALSE) {
6a488035
TO
2020 $moveToTrash = TRUE;
2021
2022 $duplicateContacts = FALSE;
2023 if ($mainContactId && $otherContactId &&
2024 $mainContactId != $otherContactId
2025 ) {
2026 $duplicateContacts = TRUE;
2027 }
2028
2029 $duplicateCases = FALSE;
2030 if ($mainCaseId && $otherCaseId &&
2031 $mainCaseId != $otherCaseId
2032 ) {
2033 $duplicateCases = TRUE;
2034 }
2035
fc9f05e0 2036 $mainCaseIds = [];
6a488035
TO
2037 if (!$duplicateContacts && !$duplicateCases) {
2038 return $mainCaseIds;
2039 }
2040
b864360d
MWMC
2041 $activityTypes = CRM_Activity_BAO_Activity::buildOptions('activity_type_id', 'validate');
2042 $completedActivityStatus = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed');
44f817d4 2043 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
9e74e3ce 2044 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
2045 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
2046 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
8ef12e64 2047
fc9f05e0 2048 $processCaseIds = [$otherCaseId];
6a488035
TO
2049 if ($duplicateContacts && !$duplicateCases) {
2050 if ($changeClient) {
fc9f05e0 2051 $processCaseIds = [$mainCaseId];
6a488035
TO
2052 }
2053 else {
2054 //get all case ids for other contact.
2055 $processCaseIds = self::retrieveCaseIdsByContactId($otherContactId, TRUE);
2056 }
2057 if (!is_array($processCaseIds)) {
2058 return;
2059 }
2060 }
2061
2062 $session = CRM_Core_Session::singleton();
2063 $currentUserId = $session->get('userID');
2064
02094cdb
JJ
2065 CRM_Utils_Hook::pre_case_merge($mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient);
2066
6a488035
TO
2067 // copy all cases and connect to main contact id.
2068 foreach ($processCaseIds as $otherCaseId) {
2069 if ($duplicateContacts) {
fc9f05e0 2070 $mainCase = CRM_Core_DAO::copyGeneric('CRM_Case_DAO_Case', ['id' => $otherCaseId]);
6a488035
TO
2071 $mainCaseId = $mainCase->id;
2072 if (!$mainCaseId) {
2073 continue;
2074 }
8bd86283 2075
6a488035
TO
2076 $mainCaseIds[] = $mainCaseId;
2077 //insert record for case contact.
2078 $otherCaseContact = new CRM_Case_DAO_CaseContact();
2079 $otherCaseContact->case_id = $otherCaseId;
2080 $otherCaseContact->find();
2081 while ($otherCaseContact->fetch()) {
2082 $mainCaseContact = new CRM_Case_DAO_CaseContact();
2083 $mainCaseContact->case_id = $mainCaseId;
2084 $mainCaseContact->contact_id = $otherCaseContact->contact_id;
2085 if ($mainCaseContact->contact_id == $otherContactId) {
2086 $mainCaseContact->contact_id = $mainContactId;
2087 }
2088 //avoid duplicate object.
2089 if (!$mainCaseContact->find(TRUE)) {
2090 $mainCaseContact->save();
2091 }
6a488035 2092 }
6a488035
TO
2093 }
2094 elseif (!$otherContactId) {
2095 $otherContactId = $mainContactId;
2096 }
2097
2098 if (!$mainCaseId || !$otherCaseId ||
2099 !$mainContactId || !$otherContactId
2100 ) {
2101 continue;
2102 }
2103
2104 // get all activities for other case.
fc9f05e0 2105 $otherCaseActivities = [];
6a488035
TO
2106 CRM_Core_DAO::commonRetrieveAll('CRM_Case_DAO_CaseActivity', 'case_id', $otherCaseId, $otherCaseActivities);
2107
2108 //for duplicate cases do not process singleton activities.
fc9f05e0 2109 $otherActivityIds = $singletonActivityIds = [];
6a488035
TO
2110 foreach ($otherCaseActivities as $caseActivityId => $otherIds) {
2111 $otherActId = CRM_Utils_Array::value('activity_id', $otherIds);
2112 if (!$otherActId || in_array($otherActId, $otherActivityIds)) {
2113 continue;
2114 }
2115 $otherActivityIds[] = $otherActId;
2116 }
2117 if ($duplicateCases) {
2118 if ($openCaseType = array_search('Open Case', $activityTypes)) {
2119 $sql = "
2120SELECT id
2121 FROM civicrm_activity
2122 WHERE activity_type_id = $openCaseType
2123 AND id IN ( " . implode(',', array_values($otherActivityIds)) . ');';
2124 $dao = CRM_Core_DAO::executeQuery($sql);
2125 while ($dao->fetch()) {
2126 $singletonActivityIds[] = $dao->id;
2127 }
6a488035
TO
2128 }
2129 }
2130
2131 // migrate all activities and connect to main contact.
fc9f05e0 2132 $copiedActivityIds = $activityMappingIds = [];
6a488035
TO
2133 sort($otherActivityIds);
2134 foreach ($otherActivityIds as $otherActivityId) {
2135
2136 //for duplicate cases -
2137 //do not migrate singleton activities.
2138 if (!$otherActivityId || in_array($otherActivityId, $singletonActivityIds)) {
2139 continue;
2140 }
2141
2142 //migrate activity record.
2143 $otherActivity = new CRM_Activity_DAO_Activity();
2144 $otherActivity->id = $otherActivityId;
2145 if (!$otherActivity->find(TRUE)) {
2146 continue;
2147 }
2148
fc9f05e0 2149 $mainActVals = [];
6a488035
TO
2150 $mainActivity = new CRM_Activity_DAO_Activity();
2151 CRM_Core_DAO::storeValues($otherActivity, $mainActVals);
2152 $mainActivity->copyValues($mainActVals);
2153 $mainActivity->id = NULL;
2154 $mainActivity->activity_date_time = CRM_Utils_Date::isoToMysql($otherActivity->activity_date_time);
6a488035
TO
2155 $mainActivity->source_record_id = CRM_Utils_Array::value($mainActivity->source_record_id,
2156 $activityMappingIds
2157 );
2158
2159 $mainActivity->original_id = CRM_Utils_Array::value($mainActivity->original_id,
2160 $activityMappingIds
2161 );
2162
2163 $mainActivity->parent_id = CRM_Utils_Array::value($mainActivity->parent_id,
2164 $activityMappingIds
2165 );
2166 $mainActivity->save();
2167 $mainActivityId = $mainActivity->id;
2168 if (!$mainActivityId) {
2169 continue;
2170 }
2171
2172 $activityMappingIds[$otherActivityId] = $mainActivityId;
4322672b 2173 // insert log of all activities
6a488035
TO
2174 CRM_Activity_BAO_Activity::logActivityAction($mainActivity);
2175
6a488035
TO
2176 $copiedActivityIds[] = $otherActivityId;
2177
2178 //create case activity record.
2179 $mainCaseActivity = new CRM_Case_DAO_CaseActivity();
2180 $mainCaseActivity->case_id = $mainCaseId;
2181 $mainCaseActivity->activity_id = $mainActivityId;
2182 $mainCaseActivity->save();
6a488035 2183
4322672b 2184 //migrate source activity.
2185 $otherSourceActivity = new CRM_Activity_DAO_ActivityContact();
2186 $otherSourceActivity->activity_id = $otherActivityId;
2187 $otherSourceActivity->record_type_id = $sourceID;
2188 $otherSourceActivity->find();
2189 while ($otherSourceActivity->fetch()) {
2190 $mainActivitySource = new CRM_Activity_DAO_ActivityContact();
2191 $mainActivitySource->record_type_id = $sourceID;
2192 $mainActivitySource->activity_id = $mainActivityId;
2193 $mainActivitySource->contact_id = $otherSourceActivity->contact_id;
2194 if ($mainActivitySource->contact_id == $otherContactId) {
2195 $mainActivitySource->contact_id = $mainContactId;
2196 }
2197 //avoid duplicate object.
2198 if (!$mainActivitySource->find(TRUE)) {
2199 $mainActivitySource->save();
2200 }
4322672b 2201 }
4322672b 2202
6a488035 2203 //migrate target activities.
4e3d3cfc 2204 $otherTargetActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2205 $otherTargetActivity->activity_id = $otherActivityId;
9e74e3ce 2206 $otherTargetActivity->record_type_id = $targetID;
6a488035
TO
2207 $otherTargetActivity->find();
2208 while ($otherTargetActivity->fetch()) {
4e3d3cfc 2209 $mainActivityTarget = new CRM_Activity_DAO_ActivityContact();
9e74e3ce 2210 $mainActivityTarget->record_type_id = $targetID;
6a488035 2211 $mainActivityTarget->activity_id = $mainActivityId;
00bf7e59 2212 $mainActivityTarget->contact_id = $otherTargetActivity->contact_id;
2213 if ($mainActivityTarget->contact_id == $otherContactId) {
2214 $mainActivityTarget->contact_id = $mainContactId;
6a488035
TO
2215 }
2216 //avoid duplicate object.
2217 if (!$mainActivityTarget->find(TRUE)) {
2218 $mainActivityTarget->save();
2219 }
6a488035 2220 }
6a488035
TO
2221
2222 //migrate assignee activities.
4e3d3cfc 2223 $otherAssigneeActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2224 $otherAssigneeActivity->activity_id = $otherActivityId;
9e74e3ce 2225 $otherAssigneeActivity->record_type_id = $assigneeID;
6a488035
TO
2226 $otherAssigneeActivity->find();
2227 while ($otherAssigneeActivity->fetch()) {
4e3d3cfc 2228 $mainAssigneeActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2229 $mainAssigneeActivity->activity_id = $mainActivityId;
9e74e3ce 2230 $mainAssigneeActivity->record_type_id = $assigneeID;
00bf7e59 2231 $mainAssigneeActivity->contact_id = $otherAssigneeActivity->contact_id;
2232 if ($mainAssigneeActivity->contact_id == $otherContactId) {
2233 $mainAssigneeActivity->contact_id = $mainContactId;
6a488035
TO
2234 }
2235 //avoid duplicate object.
2236 if (!$mainAssigneeActivity->find(TRUE)) {
2237 $mainAssigneeActivity->save();
2238 }
6a488035 2239 }
8c31eef5
D
2240
2241 // copy custom fields and attachments
fc9f05e0 2242 $aparams = [
4322672b 2243 'activityID' => $otherActivityId,
2244 'mainActivityId' => $mainActivityId,
fc9f05e0 2245 ];
8c31eef5 2246 CRM_Activity_BAO_Activity::copyExtendedActivityData($aparams);
6a488035
TO
2247 }
2248
2249 //copy case relationship.
2250 if ($duplicateContacts) {
2251 //migrate relationship records.
2252 $otherRelationship = new CRM_Contact_DAO_Relationship();
2253 $otherRelationship->case_id = $otherCaseId;
2254 $otherRelationship->find();
fc9f05e0 2255 $otherRelationshipIds = [];
6a488035 2256 while ($otherRelationship->fetch()) {
fc9f05e0 2257 $otherRelVals = [];
6a488035
TO
2258 $updateOtherRel = FALSE;
2259 CRM_Core_DAO::storeValues($otherRelationship, $otherRelVals);
2260
2261 $mainRelationship = new CRM_Contact_DAO_Relationship();
2262 $mainRelationship->copyValues($otherRelVals);
2263 $mainRelationship->id = NULL;
2264 $mainRelationship->case_id = $mainCaseId;
2265 if ($mainRelationship->contact_id_a == $otherContactId) {
2266 $updateOtherRel = TRUE;
2267 $mainRelationship->contact_id_a = $mainContactId;
2268 }
2269
2270 //case creator change only when we merge user contact.
2271 if ($mainRelationship->contact_id_b == $otherContactId) {
2272 //do not change creator for change client.
2273 if (!$changeClient) {
2274 $updateOtherRel = TRUE;
2275 $mainRelationship->contact_id_b = ($currentUserId) ? $currentUserId : $mainContactId;
2276 }
2277 }
2278 $mainRelationship->end_date = CRM_Utils_Date::isoToMysql($otherRelationship->end_date);
2279 $mainRelationship->start_date = CRM_Utils_Date::isoToMysql($otherRelationship->start_date);
2280
2281 //avoid duplicate object.
2282 if (!$mainRelationship->find(TRUE)) {
2283 $mainRelationship->save();
2284 }
6a488035
TO
2285
2286 //get the other relationship ids to update end date.
2287 if ($updateOtherRel) {
2288 $otherRelationshipIds[$otherRelationship->id] = $otherRelationship->id;
2289 }
2290 }
6a488035
TO
2291
2292 //update other relationships end dates
2293 if (!empty($otherRelationshipIds)) {
2294 $sql = 'UPDATE civicrm_relationship
2295 SET end_date = CURDATE()
2296 WHERE id IN ( ' . implode(',', $otherRelationshipIds) . ')';
2297 CRM_Core_DAO::executeQuery($sql);
2298 }
2299 }
2300
2301 //move other case to trash.
2302 $mergeCase = self::deleteCase($otherCaseId, $moveToTrash);
2303 if (!$mergeCase) {
2304 continue;
2305 }
2306
2307 $mergeActSubject = $mergeActSubjectDetails = $mergeActType = '';
2308 if ($changeClient) {
2309 $mainContactDisplayName = CRM_Contact_BAO_Contact::displayName($mainContactId);
2310 $otherContactDisplayName = CRM_Contact_BAO_Contact::displayName($otherContactId);
2311
2312 $mergeActType = array_search('Reassigned Case', $activityTypes);
2313 $mergeActSubject = ts("Case %1 reassigned client from %2 to %3. New Case ID is %4.",
fc9f05e0 2314 [
e96f025a 2315 1 => $otherCaseId,
2316 2 => $otherContactDisplayName,
2317 3 => $mainContactDisplayName,
21dfd5f5 2318 4 => $mainCaseId,
fc9f05e0 2319 ]
6a488035
TO
2320 );
2321 }
2322 elseif ($duplicateContacts) {
2323 $mergeActType = array_search('Merge Case', $activityTypes);
2324 $mergeActSubject = ts("Case %1 copied from contact id %2 to contact id %3 via merge. New Case ID is %4.",
fc9f05e0 2325 [
e96f025a 2326 1 => $otherCaseId,
2327 2 => $otherContactId,
2328 3 => $mainContactId,
21dfd5f5 2329 4 => $mainCaseId,
fc9f05e0 2330 ]
6a488035
TO
2331 );
2332 }
2333 else {
2334 $mergeActType = array_search('Merge Case', $activityTypes);
fc9f05e0 2335 $mergeActSubject = ts("Case %1 merged into case %2", [1 => $otherCaseId, 2 => $mainCaseId]);
6a488035
TO
2336 if (!empty($copiedActivityIds)) {
2337 $sql = '
2338SELECT id, subject, activity_date_time, activity_type_id
2339FROM civicrm_activity
2340WHERE id IN (' . implode(',', $copiedActivityIds) . ')';
2341 $dao = CRM_Core_DAO::executeQuery($sql);
2342 while ($dao->fetch()) {
2343 $mergeActSubjectDetails .= "{$dao->activity_date_time} :: {$activityTypes[$dao->activity_type_id]}";
2344 if ($dao->subject) {
2345 $mergeActSubjectDetails .= " :: {$dao->subject}";
2346 }
2347 $mergeActSubjectDetails .= "<br />";
2348 }
2349 }
2350 }
2351
b864360d 2352 // Create merge activity record. Source for merge activity is the logged in user's contact ID ($currentUserId).
fc9f05e0 2353 $activityParams = [
6a488035
TO
2354 'subject' => $mergeActSubject,
2355 'details' => $mergeActSubjectDetails,
b864360d 2356 'status_id' => $completedActivityStatus,
6a488035 2357 'activity_type_id' => $mergeActType,
8c677b07 2358 'source_contact_id' => $currentUserId,
6a488035 2359 'activity_date_time' => date('YmdHis'),
fc9f05e0 2360 ];
6a488035
TO
2361
2362 $mergeActivity = CRM_Activity_BAO_Activity::create($activityParams);
2363 $mergeActivityId = $mergeActivity->id;
2364 if (!$mergeActivityId) {
2365 continue;
2366 }
6a488035
TO
2367
2368 //connect merge activity to case.
fc9f05e0 2369 $mergeCaseAct = [
6a488035
TO
2370 'case_id' => $mainCaseId,
2371 'activity_id' => $mergeActivityId,
fc9f05e0 2372 ];
6a488035
TO
2373
2374 self::processCaseActivity($mergeCaseAct);
2375 }
02094cdb
JJ
2376
2377 CRM_Utils_Hook::post_case_merge($mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient);
2378
6a488035
TO
2379 return $mainCaseIds;
2380 }
2381
2382 /**
2383 * Validate contact permission for
2384 * edit/view on activity record and build links.
2385 *
64bd5a0e
TO
2386 * @param array $tplParams
2387 * Params to be sent to template for sending email.
2388 * @param array $activityParams
2389 * Info of the activity.
6a488035 2390 */
00be9182 2391 public static function buildPermissionLinks(&$tplParams, $activityParams) {
6a488035
TO
2392 $activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityParams['source_record_id'],
2393 'activity_type_id', 'id'
2394 );
2395
a7488080 2396 if (!empty($tplParams['isCaseActivity'])) {
6a488035
TO
2397 $tplParams['editActURL'] = CRM_Utils_System::url('civicrm/case/activity',
2398 "reset=1&cid={$activityParams['target_id']}&caseid={$activityParams['case_id']}&action=update&id={$activityParams['source_record_id']}", TRUE
2399 );
2400
2401 $tplParams['viewActURL'] = CRM_Utils_System::url('civicrm/case/activity/view',
2402 "reset=1&aid={$activityParams['source_record_id']}&cid={$activityParams['target_id']}&caseID={$activityParams['case_id']}", TRUE
2403 );
2404
2405 $tplParams['manageCaseURL'] = CRM_Utils_System::url('civicrm/contact/view/case',
2406 "reset=1&id={$activityParams['case_id']}&cid={$activityParams['target_id']}&action=view&context=home", TRUE
2407 );
2408 }
2409 else {
2410 $tplParams['editActURL'] = CRM_Utils_System::url('civicrm/contact/view/activity',
2411 "atype=$activityTypeId&action=update&reset=1&id={$activityParams['source_record_id']}&cid={$tplParams['contact']['contact_id']}&context=activity", TRUE
2412 );
2413
2414 $tplParams['viewActURL'] = CRM_Utils_System::url('civicrm/contact/view/activity',
2415 "atype=$activityTypeId&action=view&reset=1&id={$activityParams['source_record_id']}&cid={$tplParams['contact']['contact_id']}&context=activity", TRUE
2416 );
2417 }
2418 }
2419
2420 /**
2421 * Validate contact permission for
2422 * given operation on activity record.
2423 *
64bd5a0e
TO
2424 * @param int $activityId
2425 * Activity record id.
2426 * @param string $operation
2427 * User operation.
2428 * @param int $actTypeId
2429 * Activity type id.
2430 * @param int $contactId
2431 * Contact id/if not pass consider logged in.
2432 * @param bool $checkComponent
2433 * Do we need to check component enabled.
6a488035 2434 *
a130e045 2435 * @return bool
6a488035 2436 */
00be9182 2437 public static function checkPermission($activityId, $operation, $actTypeId = NULL, $contactId = NULL, $checkComponent = TRUE) {
6a488035
TO
2438 $allow = FALSE;
2439 if (!$actTypeId && $activityId) {
2440 $actTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id');
2441 }
2442
2443 if (!$activityId || !$operation || !$actTypeId) {
2444 return $allow;
2445 }
2446
2447 //do check for civicase component enabled.
077dbf5e
CW
2448 if ($checkComponent && !self::enabled()) {
2449 return $allow;
6a488035
TO
2450 }
2451
2452 //do check for cases.
fc9f05e0 2453 $caseActOperations = [
6a488035
TO
2454 'File On Case',
2455 'Link Cases',
2456 'Move To Case',
2457 'Copy To Case',
fc9f05e0 2458 ];
6a488035
TO
2459
2460 if (in_array($operation, $caseActOperations)) {
abd06efc
CW
2461 static $caseCount;
2462 if (!isset($caseCount)) {
eeb45e43 2463 try {
fc9f05e0 2464 $caseCount = civicrm_api3('Case', 'getcount', [
eeb45e43 2465 'check_permissions' => TRUE,
fc9f05e0 2466 'status_id' => ['!=' => 'Closed'],
eeb45e43 2467 'is_deleted' => 0,
fc9f05e0 2468 'end_date' => ['IS NULL' => 1],
2469 ]);
eeb45e43
CW
2470 }
2471 catch (CiviCRM_API3_Exception $e) {
2472 // Lack of permissions will throw an exception
2473 $caseCount = 0;
2474 }
6a488035
TO
2475 }
2476 if ($operation == 'File On Case') {
abd06efc 2477 $allow = !empty($caseCount);
6a488035
TO
2478 }
2479 else {
abd06efc 2480 $allow = ($caseCount > 1);
6a488035
TO
2481 }
2482 }
2483
fc9f05e0 2484 $actionOperations = ['view', 'edit', 'delete'];
6a488035
TO
2485 if (in_array($operation, $actionOperations)) {
2486
2487 //do cache when user has non/supper permission.
2488 static $allowOperations;
2489
2490 if (!is_array($allowOperations) ||
2491 !array_key_exists($operation, $allowOperations)
2492 ) {
2493
2494 if (!$contactId) {
2495 $session = CRM_Core_Session::singleton();
2496 $contactId = $session->get('userID');
2497 }
2498
2499 //check for permissions.
fc9f05e0 2500 $permissions = [
2501 'view' => [
6a488035
TO
2502 'access my cases and activities',
2503 'access all cases and activities',
fc9f05e0 2504 ],
2505 'edit' => [
6a488035
TO
2506 'access my cases and activities',
2507 'access all cases and activities',
fc9f05e0 2508 ],
2509 'delete' => ['delete activities'],
2510 ];
6a488035
TO
2511
2512 //check for core permission.
fc9f05e0 2513 $hasPermissions = [];
6a488035
TO
2514 $checkPermissions = CRM_Utils_Array::value($operation, $permissions);
2515 if (is_array($checkPermissions)) {
2516 foreach ($checkPermissions as $per) {
2517 if (CRM_Core_Permission::check($per)) {
2518 $hasPermissions[$operation][] = $per;
2519 }
2520 }
2521 }
2522
2523 //has permissions.
2524 if (!empty($hasPermissions)) {
2525 //need to check activity object specific.
fc9f05e0 2526 if (in_array($operation, [
e96f025a 2527 'view',
21dfd5f5 2528 'edit',
fc9f05e0 2529 ])
e96f025a 2530 ) {
6a488035
TO
2531 //do we have supper permission.
2532 if (in_array('access all cases and activities', $hasPermissions[$operation])) {
2533 $allowOperations[$operation] = $allow = TRUE;
2534 }
2535 else {
2536 //user has only access to my cases and activity.
2537 //here object specific permmions come in picture.
2538
2539 //edit - contact must be source or assignee
2540 //view - contact must be source/assignee/target
2541 $isTarget = $isAssignee = $isSource = FALSE;
44f817d4 2542 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
4322672b 2543 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
2544 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
2545 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
6a488035 2546
4e3d3cfc 2547 $target = new CRM_Activity_DAO_ActivityContact();
9e74e3ce 2548 $target->record_type_id = $targetID;
6a488035 2549 $target->activity_id = $activityId;
00bf7e59 2550 $target->contact_id = $contactId;
6a488035
TO
2551 if ($target->find(TRUE)) {
2552 $isTarget = TRUE;
2553 }
2554
4e3d3cfc 2555 $assignee = new CRM_Activity_DAO_ActivityContact();
6a488035 2556 $assignee->activity_id = $activityId;
9e74e3ce 2557 $assignee->record_type_id = $assigneeID;
00bf7e59 2558 $assignee->contact_id = $contactId;
6a488035
TO
2559 if ($assignee->find(TRUE)) {
2560 $isAssignee = TRUE;
2561 }
2562
4322672b 2563 $source = new CRM_Activity_DAO_ActivityContact();
2564 $source->activity_id = $activityId;
2565 $source->record_type_id = $sourceID;
2566 $source->contact_id = $contactId;
2567 if ($source->find(TRUE)) {
6a488035
TO
2568 $isSource = TRUE;
2569 }
2570
2571 if ($operation == 'edit') {
2572 if ($isAssignee || $isSource) {
2573 $allow = TRUE;
2574 }
2575 }
2576 if ($operation == 'view') {
2577 if ($isTarget || $isAssignee || $isSource) {
2578 $allow = TRUE;
2579 }
2580 }
2581 }
2582 }
2583 elseif (is_array($hasPermissions[$operation])) {
2584 $allowOperations[$operation] = $allow = TRUE;
2585 }
2586 }
2587 else {
2588 //contact do not have permission.
2589 $allowOperations[$operation] = FALSE;
2590 }
2591 }
2592 else {
2593 //use cache.
2594 //here contact might have supper/non permission.
2595 $allow = $allowOperations[$operation];
2596 }
2597 }
2598
2599 //do further only when operation is granted.
2600 if ($allow) {
b864360d 2601 $actTypeName = CRM_Core_PseudoConstant::getName('CRM_Activity_BAO_Activity', 'activity_type_id', $actTypeId);
6a488035
TO
2602
2603 //do not allow multiple copy / edit action.
fc9f05e0 2604 $singletonNames = [
e96f025a 2605 'Open Case',
2606 'Reassigned Case',
2607 'Merge Case',
2608 'Link Cases',
2609 'Assign Case Role',
2610 'Email',
21dfd5f5 2611 'Inbound Email',
fc9f05e0 2612 ];
6a488035
TO
2613
2614 //do not allow to delete these activities, CRM-4543
fc9f05e0 2615 $doNotDeleteNames = ['Open Case', 'Change Case Type', 'Change Case Status', 'Change Case Start Date'];
6a488035
TO
2616
2617 //allow edit operation.
fc9f05e0 2618 $allowEditNames = ['Open Case'];
6a488035 2619
426fe3c7 2620 if (CRM_Activity_BAO_Activity::checkEditInboundEmailsPermissions()) {
ee90a98c
CR
2621 $allowEditNames[] = 'Inbound Email';
2622 }
2623
6a488035 2624 // do not allow File on Case
fc9f05e0 2625 $doNotFileNames = [
e96f025a 2626 'Open Case',
2627 'Change Case Type',
2628 'Change Case Status',
2629 'Change Case Start Date',
2630 'Reassigned Case',
2631 'Merge Case',
2632 'Link Cases',
21dfd5f5 2633 'Assign Case Role',
fc9f05e0 2634 ];
6a488035
TO
2635
2636 if (in_array($actTypeName, $singletonNames)) {
2637 $allow = FALSE;
2638 if ($operation == 'File On Case') {
2639 $allow = (in_array($actTypeName, $doNotFileNames)) ? FALSE : TRUE;
2640 }
2641 if (in_array($operation, $actionOperations)) {
2642 $allow = TRUE;
2643 if ($operation == 'edit') {
2644 $allow = (in_array($actTypeName, $allowEditNames)) ? TRUE : FALSE;
2645 }
2646 elseif ($operation == 'delete') {
2647 $allow = (in_array($actTypeName, $doNotDeleteNames)) ? FALSE : TRUE;
2648 }
2649 }
2650 }
2651 if ($allow && ($operation == 'delete') &&
2652 in_array($actTypeName, $doNotDeleteNames)
2653 ) {
2654 $allow = FALSE;
2655 }
2656
2657 if ($allow && ($operation == 'File On Case') &&
2658 in_array($actTypeName, $doNotFileNames)
2659 ) {
2660 $allow = FALSE;
2661 }
2662
2663 //check settings file for masking actions
2664 //on the basis the activity types
2665 //hide Edit link if activity type is NOT editable
2666 //(special case activities).CRM-5871
2667 if ($allow && in_array($operation, $actionOperations)) {
fc9f05e0 2668 static $actionFilter = [];
6a488035
TO
2669 if (!array_key_exists($operation, $actionFilter)) {
2670 $xmlProcessor = new CRM_Case_XMLProcessor_Process();
2671 $actionFilter[$operation] = $xmlProcessor->get('Settings', 'ActivityTypes', FALSE, $operation);
2672 }
2673 if (array_key_exists($operation, $actionFilter[$operation]) &&
2674 in_array($actTypeId, $actionFilter[$operation][$operation])
2675 ) {
2676 $allow = FALSE;
2677 }
2678 }
2679 }
2680
2681 return $allow;
2682 }
2683
2684 /**
100fef9d 2685 * Since we drop 'access CiviCase', allow access
6a488035
TO
2686 * if user has 'access my cases and activities'
2687 * or 'access all cases and activities'
2688 */
00be9182 2689 public static function accessCiviCase() {
077dbf5e 2690 if (!self::enabled()) {
6a488035
TO
2691 return FALSE;
2692 }
2693
2694 if (CRM_Core_Permission::check('access my cases and activities') ||
2695 CRM_Core_Permission::check('access all cases and activities')
2696 ) {
2697 return TRUE;
2698 }
2699
2700 return FALSE;
2701 }
2702
2703 /**
d2e5d2ce 2704 * Verify user has permission to access a case.
077dbf5e
CW
2705 *
2706 * @param int $caseId
64bd5a0e
TO
2707 * @param bool $denyClosed
2708 * Set TRUE if one wants closed cases to be treated as inaccessible.
077dbf5e
CW
2709 *
2710 * @return bool
2711 */
00be9182 2712 public static function accessCase($caseId, $denyClosed = TRUE) {
077dbf5e
CW
2713 if (!$caseId || !self::enabled()) {
2714 return FALSE;
2715 }
2716
fc9f05e0 2717 $params = ['id' => $caseId, 'check_permissions' => TRUE];
3924e596 2718 if ($denyClosed && !CRM_Core_Permission::check('access all cases and activities')) {
fc9f05e0 2719 $params['status_id'] = ['!=' => 'Closed'];
546abeb1 2720 }
eeb45e43
CW
2721 try {
2722 return (bool) civicrm_api3('Case', 'getcount', $params);
2723 }
2724 catch (CiviCRM_API3_Exception $e) {
2725 // Lack of permissions will throw an exception
2726 return FALSE;
2727 }
077dbf5e
CW
2728 }
2729
6a488035 2730 /**
d2e5d2ce 2731 * Check whether activity is a case Activity.
6a488035 2732 *
64bd5a0e
TO
2733 * @param int $activityID
2734 * Activity id.
6a488035 2735 *
a130e045 2736 * @return bool
6a488035 2737 */
00be9182 2738 public static function isCaseActivity($activityID) {
6a488035
TO
2739 $isCaseActivity = FALSE;
2740 if ($activityID) {
fc9f05e0 2741 $params = [1 => [$activityID, 'Integer']];
6a488035
TO
2742 $query = "SELECT id FROM civicrm_case_activity WHERE activity_id = %1";
2743 if (CRM_Core_DAO::singleValueQuery($query, $params)) {
2744 $isCaseActivity = TRUE;
2745 }
2746 }
2747
2748 return $isCaseActivity;
2749 }
2750
2751 /**
d2e5d2ce 2752 * Get all the case type ids currently in use.
6a488035 2753 *
a6c01b45 2754 * @return array
6a488035 2755 */
00be9182 2756 public static function getUsedCaseType() {
6a488035
TO
2757 static $caseTypeIds;
2758
2759 if (!is_array($caseTypeIds)) {
2760 $query = "SELECT DISTINCT( civicrm_case.case_type_id ) FROM civicrm_case";
2761
2762 $dao = CRM_Core_DAO::executeQuery($query);
fc9f05e0 2763 $caseTypeIds = [];
6a488035
TO
2764 while ($dao->fetch()) {
2765 $typeId = explode(CRM_Core_DAO::VALUE_SEPARATOR,
2766 $dao->case_type_id
2767 );
2768 $caseTypeIds[] = $typeId[1];
2769 }
2770 }
2771
2772 return $caseTypeIds;
2773 }
2774
2775 /**
d2e5d2ce 2776 * Get all the case status ids currently in use.
6a488035 2777 *
a6c01b45 2778 * @return array
6a488035 2779 */
00be9182 2780 public static function getUsedCaseStatuses() {
6a488035
TO
2781 static $caseStatusIds;
2782
2783 if (!is_array($caseStatusIds)) {
2784 $query = "SELECT DISTINCT( civicrm_case.status_id ) FROM civicrm_case";
2785
2786 $dao = CRM_Core_DAO::executeQuery($query);
fc9f05e0 2787 $caseStatusIds = [];
6a488035
TO
2788 while ($dao->fetch()) {
2789 $caseStatusIds[] = $dao->status_id;
2790 }
2791 }
2792
2793 return $caseStatusIds;
2794 }
2795
2796 /**
d2e5d2ce 2797 * Get all the encounter medium ids currently in use.
72b3a70c 2798 *
6a488035
TO
2799 * @return array
2800 */
00be9182 2801 public static function getUsedEncounterMediums() {
6a488035
TO
2802 static $mediumIds;
2803
2804 if (!is_array($mediumIds)) {
2805 $query = "SELECT DISTINCT( civicrm_activity.medium_id ) FROM civicrm_activity";
2806
2807 $dao = CRM_Core_DAO::executeQuery($query);
fc9f05e0 2808 $mediumIds = [];
6a488035
TO
2809 while ($dao->fetch()) {
2810 $mediumIds[] = $dao->medium_id;
2811 }
2812 }
2813
2814 return $mediumIds;
2815 }
2816
2817 /**
100fef9d 2818 * Check case configuration.
6a488035 2819 *
100fef9d 2820 * @param int $contactId
77b97be7 2821 *
a6c01b45 2822 * @return array
6a488035 2823 */
00be9182 2824 public static function isCaseConfigured($contactId = NULL) {
fc9f05e0 2825 $configured = array_fill_keys(['configured', 'allowToAddNewCase', 'redirectToCaseAdmin'], FALSE);
6a488035
TO
2826
2827 //lets check for case configured.
2828 $allCasesCount = CRM_Case_BAO_Case::caseCount(NULL, FALSE);
2829 $configured['configured'] = ($allCasesCount) ? TRUE : FALSE;
2830 if (!$configured['configured']) {
2831 //do check for case type and case status.
0372ffa2 2832 $caseTypes = CRM_Case_PseudoConstant::caseType('title', FALSE);
6a488035
TO
2833 if (!empty($caseTypes)) {
2834 $configured['configured'] = TRUE;
2835 if (!$configured['configured']) {
2836 $caseStatuses = CRM_Case_PseudoConstant::caseStatus('label', FALSE);
2837 if (!empty($caseStatuses)) {
2838 $configured['configured'] = TRUE;
2839 }
2840 }
2841 }
2842 }
2843 if ($configured['configured']) {
2844 //do check for active case type and case status.
2845 $caseTypes = CRM_Case_PseudoConstant::caseType();
2846 if (!empty($caseTypes)) {
2847 $caseStatuses = CRM_Case_PseudoConstant::caseStatus();
2848 if (!empty($caseStatuses)) {
2849 $configured['allowToAddNewCase'] = TRUE;
2850 }
2851 }
2852
2853 //do we need to redirect user to case admin.
2854 if (!$configured['allowToAddNewCase'] && $contactId) {
2855 //check for current contact case count.
2856 $currentContatCasesCount = CRM_Case_BAO_Case::caseCount($contactId);
2857 //redirect user to case admin page.
2858 if (!$currentContatCasesCount) {
2859 $configured['redirectToCaseAdmin'] = TRUE;
2860 }
2861 }
2862 }
2863
2864 return $configured;
2865 }
2866
d6f468d3 2867 /**
d2e5d2ce 2868 * Used during case component enablement and during ugprade.
72b3a70c
CW
2869 *
2870 * @return bool
6a488035 2871 */
00be9182 2872 public static function createCaseViews() {
6a4257d4 2873 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
a19fc402
TO
2874 $dao = new CRM_Core_DAO();
2875
6a488035 2876 $sql = self::createCaseViewsQuery('upcoming');
6a488035
TO
2877 $dao->query($sql);
2878 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
2879 return FALSE;
2880 }
2881
2882 // Above error doesn't get caught?
2883 $doublecheck = $dao->singleValueQuery("SELECT count(id) FROM civicrm_view_case_activity_upcoming");
2884 if (is_null($doublecheck)) {
2885 return FALSE;
2886 }
2887
2888 $sql = self::createCaseViewsQuery('recent');
6a488035
TO
2889 $dao->query($sql);
2890 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
2891 return FALSE;
2892 }
2893
2894 // Above error doesn't get caught?
2895 $doublecheck = $dao->singleValueQuery("SELECT count(id) FROM civicrm_view_case_activity_recent");
2896 if (is_null($doublecheck)) {
2897 return FALSE;
2898 }
2899
2900 return TRUE;
2901 }
2902
d6f468d3 2903 /**
100fef9d 2904 * Helper function, also used by the upgrade in case of error
72b3a70c 2905 *
cde2037d
EM
2906 * @param string $section
2907 *
72b3a70c 2908 * @return string
6a488035 2909 */
00be9182 2910 public static function createCaseViewsQuery($section = 'upcoming') {
6a488035 2911 $sql = "";
0db2818f 2912 $scheduled_id = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Scheduled');
6a488035
TO
2913 switch ($section) {
2914 case 'upcoming':
2915 $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_upcoming`
2916 AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
2917 FROM civicrm_case_activity ca
2918 INNER JOIN civicrm_activity a ON ca.activity_id=a.id
66d84429
AH
2919 WHERE a.id =
2920(SELECT b.id FROM civicrm_case_activity bca
2b95a25b 2921 INNER JOIN civicrm_activity b ON bca.activity_id=b.id
2922 WHERE b.activity_date_time <= DATE_ADD( NOW(), INTERVAL 14 DAY )
2923 AND b.is_current_revision = 1 AND b.is_deleted=0 AND b.status_id = $scheduled_id
2924 AND bca.case_id = ca.case_id ORDER BY b.activity_date_time ASC LIMIT 1)";
6a488035
TO
2925 break;
2926
2927 case 'recent':
2928 $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_recent`
2929 AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
2930 FROM civicrm_case_activity ca
2931 INNER JOIN civicrm_activity a ON ca.activity_id=a.id
66d84429
AH
2932 WHERE a.id =
2933(SELECT b.id FROM civicrm_case_activity bca
2b95a25b 2934 INNER JOIN civicrm_activity b ON bca.activity_id=b.id
2935 WHERE b.activity_date_time >= DATE_SUB( NOW(), INTERVAL 14 DAY )
2936 AND b.is_current_revision = 1 AND b.is_deleted=0 AND b.status_id <> $scheduled_id
2937 AND bca.case_id = ca.case_id ORDER BY b.activity_date_time DESC LIMIT 1)";
6a488035
TO
2938 break;
2939 }
6a488035 2940 return $sql;
e96f025a 2941 }
2942
2943 /**
100fef9d 2944 * Add/copy relationships, when new client is added for a case
e96f025a 2945 *
64bd5a0e
TO
2946 * @param int $caseId
2947 * Case id.
2948 * @param int $contactId
2949 * Contact id / new client id.
e96f025a 2950 */
00be9182 2951 public static function addCaseRelationships($caseId, $contactId) {
44466d80
KJ
2952 // get the case role / relationships for the case
2953 $caseRelationships = new CRM_Contact_DAO_Relationship();
2954 $caseRelationships->case_id = $caseId;
2955 $caseRelationships->find();
fc9f05e0 2956 $relationshipTypes = [];
44466d80
KJ
2957
2958 // make sure we don't add duplicate relationships of same relationship type.
2959 while ($caseRelationships->fetch() && !in_array($caseRelationships->relationship_type_id, $relationshipTypes)) {
fc9f05e0 2960 $values = [];
44466d80
KJ
2961 CRM_Core_DAO::storeValues($caseRelationships, $values);
2962
2963 // add relationship for new client.
2964 $newRelationship = new CRM_Contact_DAO_Relationship();
2965 $newRelationship->copyValues($values);
2966 $newRelationship->id = NULL;
2967 $newRelationship->case_id = $caseId;
2968 $newRelationship->contact_id_a = $contactId;
2969 $newRelationship->end_date = CRM_Utils_Date::isoToMysql($caseRelationships->end_date);
2970 $newRelationship->start_date = CRM_Utils_Date::isoToMysql($caseRelationships->start_date);
2971
2972 // another check to avoid duplicate relationship, in cases where client is removed and re-added again.
2973 if (!$newRelationship->find(TRUE)) {
2974 $newRelationship->save();
2975 }
44466d80
KJ
2976
2977 // store relationship type of newly created relationship
2978 $relationshipTypes[] = $caseRelationships->relationship_type_id;
2979 }
6a488035 2980 }
14a679f1
KJ
2981
2982 /**
d2e5d2ce 2983 * Get the list of clients for a case.
14a679f1
KJ
2984 *
2985 * @param int $caseId
2986 *
a6c01b45
CW
2987 * @return array
2988 * associated array with client ids
14a679f1 2989 */
00be9182 2990 public static function getCaseClients($caseId) {
fc9f05e0 2991 $clients = [];
14a679f1
KJ
2992 $caseContact = new CRM_Case_DAO_CaseContact();
2993 $caseContact->case_id = $caseId;
78762324 2994 $caseContact->orderBy('id');
14a679f1
KJ
2995 $caseContact->find();
2996
e96f025a 2997 while ($caseContact->fetch()) {
14a679f1
KJ
2998 $clients[] = $caseContact->contact_id;
2999 }
3000
3001 return $clients;
3002 }
16c0ec8d 3003
3b1c37fe
CW
3004 /**
3005 * @param int $caseId
3006 * @param string $direction
3007 * @param int $cid
3008 * @param int $relTypeId
fc9f05e0 3009 *
3b1c37fe
CW
3010 * @throws \CRM_Core_Exception
3011 * @throws \CiviCRM_API3_Exception
3012 */
3013 public static function endCaseRole($caseId, $direction, $cid, $relTypeId) {
3014 // Validate inputs
3015 if ($direction !== 'a' && $direction !== 'b') {
3016 throw new CRM_Core_Exception('Invalid relationship direction');
3017 }
3018
3019 // This case might have multiple clients, so we lookup by relationship instead of by id to get them all
3020 $sql = "SELECT id FROM civicrm_relationship WHERE case_id = %1 AND contact_id_{$direction} = %2 AND relationship_type_id = %3";
fc9f05e0 3021 $dao = CRM_Core_DAO::executeQuery($sql, [
3022 1 => [$caseId, 'Positive'],
3023 2 => [$cid, 'Positive'],
3024 3 => [$relTypeId, 'Positive'],
3025 ]);
3b1c37fe 3026 while ($dao->fetch()) {
fc9f05e0 3027 civicrm_api3('relationship', 'create', [
3b1c37fe
CW
3028 'id' => $dao->id,
3029 'is_active' => 0,
3030 'end_date' => 'now',
fc9f05e0 3031 ]);
3b1c37fe
CW
3032 }
3033 }
3034
16c0ec8d
CW
3035 /**
3036 * Get options for a given case field.
16c0ec8d 3037 *
64bd5a0e
TO
3038 * @param string $fieldName
3039 * @param string $context
64bd5a0e 3040 * @param array $props
72b3a70c 3041 * Whatever is known about this dao object.
77b97be7 3042 *
a130e045 3043 * @return array|bool
fc9f05e0 3044 * @throws \CiviCRM_API3_Exception
3045 *
3046 * @see CRM_Core_DAO::buildOptionsContext
3047 * @see CRM_Core_DAO::buildOptions
3048 *
16c0ec8d 3049 */
fc9f05e0 3050 public static function buildOptions($fieldName, $context = NULL, $props = []) {
16c0ec8d 3051 $className = __CLASS__;
fc9f05e0 3052 $params = [];
16c0ec8d
CW
3053 switch ($fieldName) {
3054 // This field is not part of this object but the api supports it
3055 case 'medium_id':
3056 $className = 'CRM_Activity_BAO_Activity';
3057 break;
31c28ed5
CW
3058
3059 // Filter status id by case type id
3060 case 'status_id':
3061 if (!empty($props['case_type_id'])) {
3062 $idField = is_numeric($props['case_type_id']) ? 'id' : 'name';
fc9f05e0 3063 $caseType = civicrm_api3('CaseType', 'getsingle', [$idField => $props['case_type_id'], 'return' => 'definition']);
31c28ed5
CW
3064 if (!empty($caseType['definition']['statuses'])) {
3065 $params['condition'] = 'v.name IN ("' . implode('","', $caseType['definition']['statuses']) . '")';
3066 }
3067 }
3068 break;
16c0ec8d
CW
3069 }
3070 return CRM_Core_PseudoConstant::get($className, $fieldName, $params, $context);
3071 }
96025800 3072
174a1918
CW
3073 /**
3074 * @inheritDoc
3075 */
20e41014 3076 public function addSelectWhereClause() {
0b80f0b4
CW
3077 // We always return an array with these keys, even if they are empty,
3078 // because this tells the query builder that we have considered these fields for acls
fc9f05e0 3079 $clauses = [
3080 'id' => [],
ff9340a4 3081 // Only case admins can view deleted cases
fc9f05e0 3082 'is_deleted' => CRM_Core_Permission::check('administer CiviCase') ? [] : ["= 0"],
3083 ];
174a1918 3084 // Ensure the user has permission to view the case client
d1d3c04a 3085 $contactClause = CRM_Utils_SQL::mergeSubquery('Contact');
ff9340a4
CW
3086 if ($contactClause) {
3087 $contactClause = implode(' AND contact_id ', $contactClause);
3088 $clauses['id'][] = "IN (SELECT case_id FROM civicrm_case_contact WHERE contact_id $contactClause)";
174a1918 3089 }
0b80f0b4 3090 // The api gatekeeper ensures the user has at least "access my cases and activities"
174a1918
CW
3091 // so if they do not have permission to see all cases we'll assume they can only access their own
3092 if (!CRM_Core_Permission::check('access all cases and activities')) {
3093 $user = (int) CRM_Core_Session::getLoggedInContactID();
ff9340a4 3094 $clauses['id'][] = "IN (
174a1918 3095 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 3096 (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
3097 )
3098 )";
3099 }
2b240c0c 3100 CRM_Utils_Hook::selectWhereClause($this, $clauses);
ff9340a4 3101 return $clauses;
174a1918
CW
3102 }
3103
8aeeea00 3104 /**
3cf1fae9 3105 * CRM-20308: Method to get the contact id to use as from contact for email copy
8aeeea00
EH
3106 * 1. Activity Added by Contact's email address
3107 * 2. System Default From Address
3108 * 3. Default Organization Contact email address
3109 * 4. Logged in user
3110 *
3cf1fae9 3111 * @param int $activityID
3112 *
8aeeea00 3113 * @return mixed $emailFromContactId
fc9f05e0 3114 *
3115 * @throws \CiviCRM_API3_Exception
8aeeea00
EH
3116 * @see https://issues.civicrm.org/jira/browse/CRM-20308
3117 */
3cf1fae9 3118 public static function getReceiptFrom($activityID) {
3119 $name = $address = NULL;
3120
4c981f37
MW
3121 if (!empty($activityID) && (Civi::settings()->get('allow_mail_from_logged_in_contact'))) {
3122 // This breaks SPF/DMARC if email is sent from an email address that the server is not authorised to send from.
3123 // so we can disable this behaviour with the "allow_mail_from_logged_in_contact" setting.
3cf1fae9 3124 // There is always a 'Added by' contact for a activity,
3125 // so we can safely use ActivityContact.Getvalue API
fc9f05e0 3126 $sourceContactId = civicrm_api3('ActivityContact', 'getvalue', [
3cf1fae9 3127 'activity_id' => $activityID,
3128 'record_type_id' => 'Activity Source',
3129 'return' => 'contact_id',
fc9f05e0 3130 ]);
3cf1fae9 3131 list($name, $address) = CRM_Contact_BAO_Contact_Location::getEmailDetails($sourceContactId);
8aeeea00 3132 }
3cf1fae9 3133
3134 // If 'From' email address not found for Source Activity Contact then
3135 // fetch the email from domain or logged in user.
3136 if (empty($address)) {
3137 list($name, $address) = CRM_Core_BAO_Domain::getDefaultReceiptFrom();
8aeeea00 3138 }
3cf1fae9 3139
8aeeea00
EH
3140 return "$name <$address>";
3141 }
3142
1d6f94ab
CW
3143 /**
3144 * @return array
3145 */
3146 public static function getEntityRefFilters() {
3147 $filters = [
3148 [
3149 'key' => 'case_id.case_type_id',
3150 'value' => ts('Case Type'),
3151 'entity' => 'Case',
3152 ],
3153 [
3154 'key' => 'case_id.status_id',
3155 'value' => ts('Case Status'),
3156 'entity' => 'Case',
3157 ],
3158 ];
3159 foreach (CRM_Contact_BAO_Contact::getEntityRefFilters() as $filter) {
2229cf4f 3160 $filter += ['entity' => 'Contact'];
1d6f94ab
CW
3161 $filter['key'] = 'contact_id.' . $filter['key'];
3162 $filters[] = $filter;
3163 }
3164 return $filters;
3165 }
3166
41cf58d3
AF
3167 /**
3168 * Fetch Case Role direction from Case Type
3169 */
3170 public static function getCaseRoleDirection($caseId, $roleTypeId = NULL) {
3171 try {
fc9f05e0 3172 $case = civicrm_api3('Case', 'getsingle', ['id' => $caseId]);
41cf58d3
AF
3173 }
3174 catch (CiviCRM_API3_Exception $e) {
3175 // Lack of permissions will throw an exception
3176 return 0;
3177 }
3178 if (!empty($case['case_type_id'])) {
3179 try {
fc9f05e0 3180 $caseType = civicrm_api3('CaseType', 'getsingle', ['id' => $case['case_type_id'], 'return' => ['definition']]);
41cf58d3
AF
3181 }
3182 catch (CiviCRM_API3_Exception $e) {
3183 // Lack of permissions will throw an exception
3184 return 'no case type found';
3185 }
3186 if (!empty($caseType['definition']['caseRoles'])) {
fc9f05e0 3187 $caseRoles = [];
41cf58d3
AF
3188 foreach ($caseType['definition']['caseRoles'] as $key => $roleDetails) {
3189 // Check if its an a_b label
3190 try {
fc9f05e0 3191 $relType = civicrm_api3('RelationshipType', 'getsingle', ['label_a_b' => $roleDetails['name']]);
41cf58d3
AF
3192 }
3193 catch (CiviCRM_API3_Exception $e) {
3194 }
3195 if (!empty($relType['id'])) {
3196 $roleDetails['id'] = $relType['id'];
d0a94888 3197 $roleDetails['direction'] = 'b_a';
41cf58d3
AF
3198 }
3199 // Check if its a b_a label
3200 try {
fc9f05e0 3201 $relTypeBa = civicrm_api3('RelationshipType', 'getsingle', ['label_b_a' => $roleDetails['name']]);
41cf58d3
AF
3202 }
3203 catch (CiviCRM_API3_Exception $e) {
3204 }
3205 if (!empty($relTypeBa['id'])) {
3206 if (!empty($roleDetails['direction'])) {
3207 $roleDetails['direction'] = 'bidrectional';
3208 }
3209 else {
3210 $roleDetails['id'] = $relTypeBa['id'];
d0a94888 3211 $roleDetails['direction'] = 'a_b';
41cf58d3
AF
3212 }
3213 }
3214 $caseRoles[$roleDetails['id']] = $roleDetails;
3215 }
3216 }
3217 return $caseRoles;
3218 }
3219 }
3220
6a488035 3221}