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