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