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