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