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