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