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