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