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