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