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