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