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