Merge pull request #6889 from davecivicrm/CRM-17175
[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
1764 self::$_exportableFields = $fields;
1765 }
1766 return self::$_exportableFields;
1767 }
1768
1769 /**
d2e5d2ce 1770 * Restore the record that are associated with this case.
6a488035 1771 *
64bd5a0e
TO
1772 * @param int $caseId
1773 * Id of the case to restore.
6a488035 1774 *
72b3a70c 1775 * @return bool
6a488035 1776 */
00be9182 1777 public static function restoreCase($caseId) {
6a488035
TO
1778 //restore activities
1779 $activities = self::getCaseActivityDates($caseId);
1780 if ($activities) {
1781 foreach ($activities as $value) {
1782 CRM_Activity_BAO_Activity::restoreActivity($value);
1783 }
1784 }
1785 //restore case
e96f025a 1786 $case = new CRM_Case_DAO_Case();
1787 $case->id = $caseId;
6a488035
TO
1788 $case->is_deleted = 0;
1789 $case->save();
1790
1791 //CRM-7364, enable relationships
1792 self::enableDisableCaseRelationships($caseId, TRUE);
1793 return TRUE;
1794 }
1795
4c6ce474
EM
1796 /**
1797 * @param $groupInfo
1798 * @param null $sort
1799 * @param null $showLinks
1800 * @param bool $returnOnlyCount
1801 * @param int $offset
1802 * @param int $rowCount
1803 *
1804 * @return array
1805 */
00be9182 1806 public static function getGlobalContacts(&$groupInfo, $sort = NULL, $showLinks = NULL, $returnOnlyCount = FALSE, $offset = 0, $rowCount = 25) {
6a488035
TO
1807 $globalContacts = array();
1808
1809 $settingsProcessor = new CRM_Case_XMLProcessor_Settings();
1810 $settings = $settingsProcessor->run();
1811 if (!empty($settings)) {
1812 $groupInfo['name'] = $settings['groupname'];
1813 if ($groupInfo['name']) {
1814 $searchParams = array('name' => $groupInfo['name']);
1815 $results = array();
1816 CRM_Contact_BAO_Group::retrieve($searchParams, $results);
1817 if ($results) {
e96f025a 1818 $groupInfo['id'] = $results['id'];
6a488035 1819 $groupInfo['title'] = $results['title'];
b3188db2 1820 $params = array(array('group', '=', $groupInfo['id'], 0, 0));
e96f025a 1821 $return = array('contact_id' => 1, 'sort_name' => 1, 'display_name' => 1, 'email' => 1, 'phone' => 1);
d79c94d5 1822 list($globalContacts) = CRM_Contact_BAO_Query::apiQuery($params, $return, NULL, $sort, $offset, $rowCount, TRUE, $returnOnlyCount);
6a488035
TO
1823
1824 if ($returnOnlyCount) {
1825 return $globalContacts;
1826 }
1827
1828 if ($showLinks) {
e96f025a 1829 foreach ($globalContacts as $idx => $contact) {
d79c94d5 1830 $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
1831 }
1832 }
1833 }
1834 }
1835 }
1836 return $globalContacts;
1837 }
1838
4c6ce474 1839 /**
d2e5d2ce 1840 * Convenience function to get both case contacts and global in one array.
100fef9d 1841 * @param int $caseId
4c6ce474
EM
1842 *
1843 * @return array
1844 */
00be9182 1845 public static function getRelatedAndGlobalContacts($caseId) {
6a488035
TO
1846 $relatedContacts = self::getRelatedContacts($caseId);
1847
1848 $groupInfo = array();
1849 $globalContacts = self::getGlobalContacts($groupInfo);
1850
1851 //unset values which are not required.
1852 foreach ($globalContacts as $k => & $v) {
1853 unset($v['email_id']);
1854 unset($v['group_contact_id']);
1855 unset($v['status']);
1856 unset($v['phone']);
1857 $v['role'] = $groupInfo['title'];
1858 }
1859 //include multiple listings for the same contact/different roles.
1860 $relatedGlobalContacts = array_merge($relatedContacts, $globalContacts);
1861 return $relatedGlobalContacts;
1862 }
1863
1864 /**
100fef9d 1865 * Get Case ActivitiesDueDates with given criteria.
6a488035 1866 *
64bd5a0e
TO
1867 * @param int $caseID
1868 * Case id.
1869 * @param array $criteriaParams
1870 * Given criteria.
1871 * @param bool $latestDate
72b3a70c 1872 * If set newest or oldest date is selected.
6a488035 1873 *
72b3a70c
CW
1874 * @return array
1875 * case activities due dates
6a488035 1876 *
6a488035 1877 */
00be9182 1878 public static function getCaseActivityDates($caseID, $criteriaParams = array(), $latestDate = FALSE) {
e96f025a 1879 $values = array();
6a488035 1880 $selectDate = " ca.activity_date_time";
e96f025a 1881 $where = $groupBy = ' ';
6a488035
TO
1882
1883 if (!$caseID) {
408b79bf 1884 return NULL;
6a488035
TO
1885 }
1886
1887 if ($latestDate) {
a7488080 1888 if (!empty($criteriaParams['activity_type_id'])) {
6a488035
TO
1889 $where .= " AND ca.activity_type_id = " . CRM_Utils_Type::escape($criteriaParams['activity_type_id'], 'Integer');
1890 $where .= " AND ca.is_current_revision = 1";
1891 $groupBy .= " GROUP BY ca.activity_type_id";
1892 }
1893
a7488080 1894 if (!empty($criteriaParams['newest'])) {
6a488035
TO
1895 $selectDate = " max(ca.activity_date_time) ";
1896 }
1897 else {
1898 $selectDate = " min(ca.activity_date_time) ";
1899 }
1900 }
1901
1902 $query = "SELECT ca.id, {$selectDate} as activity_date
1903 FROM civicrm_activity ca
1904 LEFT JOIN civicrm_case_activity cca ON cca.activity_id = ca.id LEFT JOIN civicrm_case cc ON cc.id = cca.case_id
1905 WHERE cc.id = %1 {$where} {$groupBy}";
1906
1907 $params = array(1 => array($caseID, 'Integer'));
1908 $dao = CRM_Core_DAO::executeQuery($query, $params);
1909
1910 while ($dao->fetch()) {
1911 $values[$dao->id]['id'] = $dao->id;
1912 $values[$dao->id]['activity_date'] = $dao->activity_date;
1913 }
1914 $dao->free();
1915 return $values;
1916 }
1917
1918 /**
100fef9d 1919 * Create activities when Case or Other roles assigned/modified/deleted.
6a488035 1920 *
100fef9d 1921 * @param int $caseId
64bd5a0e
TO
1922 * @param int $relationshipId
1923 * Relationship id.
1924 * @param int $relContactId
1925 * Case role assignee contactId.
100fef9d 1926 * @param int $contactId
6a488035 1927 */
00be9182 1928 public static function createCaseRoleActivity($caseId, $relationshipId, $relContactId = NULL, $contactId = NULL) {
6a488035
TO
1929 if (!$caseId || !$relationshipId || empty($relationshipId)) {
1930 return;
1931 }
1932
1933 $queryParam = array();
1934 if (is_array($relationshipId)) {
1935 $relationshipId = implode(',', $relationshipId);
1936 $relationshipClause = " civicrm_relationship.id IN ($relationshipId)";
1937 }
1938 else {
1939 $relationshipClause = " civicrm_relationship.id = %1";
1940 $queryParam[1] = array($relationshipId, 'Positive');
1941 }
1942
1943 $query = "
1944 SELECT cc.display_name as clientName,
1945 cca.display_name as assigneeContactName,
1946 civicrm_relationship.case_id as caseId,
1947 civicrm_relationship_type.label_a_b as relation_a_b,
1948 civicrm_relationship_type.label_b_a as relation_b_a,
1949 civicrm_relationship.contact_id_b as rel_contact_id,
1950 civicrm_relationship.contact_id_a as assign_contact_id
1951 FROM civicrm_relationship_type, civicrm_relationship
1952 LEFT JOIN civicrm_contact cc ON cc.id = civicrm_relationship.contact_id_b
1953 LEFT JOIN civicrm_contact cca ON cca.id = civicrm_relationship.contact_id_a
1954 WHERE civicrm_relationship.relationship_type_id = civicrm_relationship_type.id AND {$relationshipClause}";
1955
1956 $dao = CRM_Core_DAO::executeQuery($query, $queryParam);
1957
1958 while ($dao->fetch()) {
1959 //to get valid assignee contact(s).
1960 if (isset($dao->caseId) || $dao->rel_contact_id != $contactId) {
1961 $caseRelationship = $dao->relation_a_b;
1962 $assigneContactName = $dao->clientName;
1963 $assigneContactIds[$dao->rel_contact_id] = $dao->rel_contact_id;
1964 }
1965 else {
1966 $caseRelationship = $dao->relation_b_a;
1967 $assigneContactName = $dao->assigneeContactName;
1968 $assigneContactIds[$dao->assign_contact_id] = $dao->assign_contact_id;
1969 }
1970 }
1971
1972 $session = CRM_Core_Session::singleton();
1973 $activityParams = array(
1974 'source_contact_id' => $session->get('userID'),
1975 'subject' => $caseRelationship . ' : ' . $assigneContactName,
1976 'activity_date_time' => date('YmdHis'),
1977 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'),
1978 );
1979
1980 //if $relContactId is passed, role is added or modified.
1981 if (!empty($relContactId)) {
1982 $activityParams['assignee_contact_id'] = $assigneContactIds;
1983
1984 $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type',
1985 'Assign Case Role',
1986 'name'
1987 );
1988 }
1989 else {
1990 $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type',
1991 'Remove Case Role',
1992 'name'
1993 );
1994 }
1995
1996 $activityParams['activity_type_id'] = $activityTypeID;
1997
1998 $activity = CRM_Activity_BAO_Activity::create($activityParams);
1999
2000 //create case_activity record.
2001 $caseParams = array(
2002 'activity_id' => $activity->id,
2003 'case_id' => $caseId,
2004 );
2005
2006 CRM_Case_BAO_Case::processCaseActivity($caseParams);
2007 }
2008
2009 /**
100fef9d 2010 * Get case manger
6a488035
TO
2011 * contact which is assigned a case role of case manager.
2012 *
64bd5a0e
TO
2013 * @param int $caseType
2014 * Case type.
2015 * @param int $caseId
2016 * Case id.
6a488035 2017 *
a6c01b45
CW
2018 * @return array
2019 * array of contact on success otherwise empty
6a488035 2020 *
6a488035 2021 */
00be9182 2022 public static function getCaseManagerContact($caseType, $caseId) {
6a488035 2023 if (!$caseType || !$caseId) {
408b79bf 2024 return NULL;
6a488035
TO
2025 }
2026
2027 $caseManagerContact = array();
2028 $xmlProcessor = new CRM_Case_XMLProcessor_Process();
2029
2030 $managerRoleId = $xmlProcessor->getCaseManagerRoleId($caseType);
2031
2032 if (!empty($managerRoleId)) {
2033 $managerRoleQuery = "
2034SELECT civicrm_contact.id as casemanager_id,
2035 civicrm_contact.sort_name as casemanager
2036 FROM civicrm_contact
2037 LEFT JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = civicrm_contact.id AND civicrm_relationship.relationship_type_id = %1)
2038 LEFT JOIN civicrm_case ON civicrm_case.id = civicrm_relationship.case_id
2039 WHERE civicrm_case.id = %2";
2040
2041 $managerRoleParams = array(
2042 1 => array($managerRoleId, 'Integer'),
2043 2 => array($caseId, 'Integer'),
2044 );
2045
2046 $dao = CRM_Core_DAO::executeQuery($managerRoleQuery, $managerRoleParams);
2047 if ($dao->fetch()) {
2048 $caseManagerContact['casemanager_id'] = $dao->casemanager_id;
2049 $caseManagerContact['casemanager'] = $dao->casemanager;
2050 }
2051 }
2052
2053 return $caseManagerContact;
2054 }
2055
2056 /**
cde2037d 2057 * Get all cases with no end dates.
6a488035 2058 *
77b97be7
EM
2059 * @param array $params
2060 * @param array $excludeCaseIds
2061 * @param bool $excludeDeleted
2062 *
cde2037d
EM
2063 * @param bool $includeClosed
2064 *
a6c01b45 2065 * @return array
cde2037d 2066 * Case and related data keyed on case id
6a488035 2067 */
00be9182 2068 public static function getUnclosedCases($params = array(), $excludeCaseIds = array(), $excludeDeleted = TRUE, $includeClosed = FALSE) {
6a488035 2069 //params from ajax call.
3c0b6a40 2070 $where = array($includeClosed ? '(1)' : '(ca.end_date is null)');
6a488035 2071 if ($caseType = CRM_Utils_Array::value('case_type', $params)) {
8ffdec17 2072 $where[] = "( civicrm_case_type.title LIKE '%$caseType%' )";
6a488035
TO
2073 }
2074 if ($sortName = CRM_Utils_Array::value('sort_name', $params)) {
e96f025a 2075 $config = CRM_Core_Config::singleton();
2076 $search = ($config->includeWildCardInName) ? "%$sortName%" : "$sortName%";
6a488035
TO
2077 $where[] = "( sort_name LIKE '$search' )";
2078 }
bd343dcc
CW
2079 if ($cid = CRM_Utils_Array::value('contact_id', $params)) {
2080 $where[] = " c.id = $cid ";
2081 }
6a488035
TO
2082 if (is_array($excludeCaseIds) &&
2083 !CRM_Utils_System::isNull($excludeCaseIds)
2084 ) {
2085 $where[] = ' ( ca.id NOT IN ( ' . implode(',', $excludeCaseIds) . ' ) ) ';
2086 }
2087 if ($excludeDeleted) {
2088 $where[] = ' ( ca.is_deleted = 0 OR ca.is_deleted IS NULL ) ';
2089 }
2090
2091 //filter for permissioned cases.
2092 $filterCases = array();
2093 $doFilterCases = FALSE;
2094 if (!CRM_Core_Permission::check('access all cases and activities')) {
2095 $doFilterCases = TRUE;
e96f025a 2096 $session = CRM_Core_Session::singleton();
2097 $filterCases = CRM_Case_BAO_Case::getCases(FALSE, $session->get('userID'));
6a488035
TO
2098 }
2099 $whereClause = implode(' AND ', $where);
2100
2101 $limitClause = '';
2102 if ($limit = CRM_Utils_Array::value('limit', $params)) {
2103 $limitClause = "LIMIT 0, $limit";
2104 }
2105
2106 $query = "
2107 SELECT c.id as contact_id,
2108 c.sort_name,
2109 ca.id,
2110 ca.subject as case_subject,
8ffdec17 2111 civicrm_case_type.title as case_type,
3c0b6a40
CW
2112 ca.start_date as start_date,
2113 ca.end_date as end_date,
2114 ca.status_id
6a488035
TO
2115 FROM civicrm_case ca INNER JOIN civicrm_case_contact cc ON ca.id=cc.case_id
2116 INNER JOIN civicrm_contact c ON cc.contact_id=c.id
8ffdec17 2117 INNER JOIN civicrm_case_type ON ca.case_type_id = civicrm_case_type.id
6a488035 2118 WHERE {$whereClause}
3c0b6a40 2119 ORDER BY c.sort_name, ca.end_date
6a488035
TO
2120 {$limitClause}
2121";
2122 $dao = CRM_Core_DAO::executeQuery($query);
16fb2340
C
2123 $statuses = CRM_Case_BAO_Case::buildOptions('status_id', 'create');
2124
6a488035
TO
2125 $unclosedCases = array();
2126 while ($dao->fetch()) {
2127 if ($doFilterCases && !array_key_exists($dao->id, $filterCases)) {
2128 continue;
2129 }
2130 $unclosedCases[$dao->id] = array(
2131 'sort_name' => $dao->sort_name,
2132 'case_type' => $dao->case_type,
2133 'contact_id' => $dao->contact_id,
2134 'start_date' => $dao->start_date,
3c0b6a40 2135 'end_date' => $dao->end_date,
6a488035 2136 'case_subject' => $dao->case_subject,
3c0b6a40 2137 'case_status' => $statuses[$dao->status_id],
6a488035
TO
2138 );
2139 }
2140 $dao->free();
2141
2142 return $unclosedCases;
2143 }
2144
4c6ce474 2145 /**
100fef9d 2146 * @param int $contactId
4c6ce474
EM
2147 * @param bool $excludeDeleted
2148 *
2149 * @return null|string
2150 */
00be9182 2151 public static function caseCount($contactId = NULL, $excludeDeleted = TRUE) {
6a488035
TO
2152 $whereConditions = array();
2153 if ($excludeDeleted) {
2154 $whereConditions[] = "( civicrm_case.is_deleted = 0 OR civicrm_case.is_deleted IS NULL )";
2155 }
2156 if ($contactId) {
2157 $whereConditions[] = "civicrm_case_contact.contact_id = {$contactId}";
2158 }
2159 if (!CRM_Core_Permission::check('access all cases and activities')) {
2160 static $accessibleCaseIds;
2161 if (!is_array($accessibleCaseIds)) {
2162 $session = CRM_Core_Session::singleton();
ef0ae32d 2163 $accessibleCaseIds = array_keys(self::getCases(FALSE, $session->get('userID'), 'any'));
6a488035
TO
2164 }
2165 //no need of further processing.
2166 if (empty($accessibleCaseIds)) {
2167 return 0;
2168 }
2169 $whereConditions[] = "( civicrm_case.id in (" . implode(',', $accessibleCaseIds) . ") )";
2170 }
2171
2172 $whereClause = '';
2173 if (!empty($whereConditions)) {
2174 $whereClause = "WHERE " . implode(' AND ', $whereConditions);
2175 }
2176
2177 $query = "
2178 SELECT count( civicrm_case.id )
2179 FROM civicrm_case
2180LEFT JOIN civicrm_case_contact ON ( civicrm_case.id = civicrm_case_contact.case_id )
2181 {$whereClause}";
2182
2183 return CRM_Core_DAO::singleValueQuery($query);
2184 }
2185
2186 /**
2187 * Retrieve cases related to particular contact.
2188 *
64bd5a0e
TO
2189 * @param int $contactId
2190 * Contact id.
2191 * @param bool $excludeDeleted
2192 * Do not include deleted cases.
6a488035 2193 *
72b3a70c 2194 * @return array
6a488035 2195 */
00be9182 2196 public static function getContactCases($contactId, $excludeDeleted = TRUE) {
6a488035
TO
2197 $cases = array();
2198 if (!$contactId) {
2199 return $cases;
2200 }
2201
2202 $whereClause = "civicrm_case_contact.contact_id = %1";
2203 if ($excludeDeleted) {
2204 $whereClause .= " AND ( civicrm_case.is_deleted = 0 OR civicrm_case.is_deleted IS NULL )";
2205 }
2206
2207 $query = "
8ffdec17 2208 SELECT civicrm_case.id, civicrm_case_type.title as case_type, civicrm_case.start_date
6a488035
TO
2209 FROM civicrm_case
2210INNER JOIN civicrm_case_contact ON ( civicrm_case.id = civicrm_case_contact.case_id )
8ffdec17 2211 LEFT JOIN civicrm_case_type ON civicrm_case.case_type_id = civicrm_case_type.id
6a488035
TO
2212 WHERE {$whereClause}";
2213
2214 $dao = CRM_Core_DAO::executeQuery($query, array(1 => array($contactId, 'Integer')));
2215 while ($dao->fetch()) {
2216 $cases[$dao->id] = array(
2217 'case_id' => $dao->id,
2218 'case_type' => $dao->case_type,
2219 'case_start_date' => $dao->start_date,
2220 );
2221 }
2222 $dao->free();
2223
2224 return $cases;
2225 }
2226
2227 /**
2228 * Retrieve related cases for give case.
2229 *
64bd5a0e
TO
2230 * @param int $mainCaseId
2231 * Id of main case.
2232 * @param int $contactId
2233 * Id of contact.
2234 * @param bool $excludeDeleted
2235 * Do not include deleted cases.
6a488035 2236 *
72b3a70c 2237 * @return array
6a488035 2238 */
00be9182 2239 public static function getRelatedCases($mainCaseId, $contactId, $excludeDeleted = TRUE) {
6a488035
TO
2240 //FIXME : do check for permissions.
2241
2242 $relatedCases = array();
2243 if (!$mainCaseId || !$contactId) {
2244 return $relatedCases;
2245 }
2246
2247 $linkActType = array_search('Link Cases',
2248 CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name')
2249 );
2250 if (!$linkActType) {
2251 return $relatedCases;
2252 }
2253
2254 $whereClause = "mainCase.id = %2";
2255 if ($excludeDeleted) {
2256 $whereClause .= " AND ( relAct.is_deleted = 0 OR relAct.is_deleted IS NULL )";
2257 }
2258
2259 //1. first fetch related case ids.
2260 $query = "
2261 SELECT relCaseAct.case_id
2262 FROM civicrm_case mainCase
2263 INNER JOIN civicrm_case_activity mainCaseAct ON (mainCaseAct.case_id = mainCase.id)
2264 INNER JOIN civicrm_activity mainAct ON (mainCaseAct.activity_id = mainAct.id AND mainAct.activity_type_id = %1)
2265 INNER JOIN civicrm_case_activity relCaseAct ON (relCaseAct.activity_id = mainAct.id AND mainCaseAct.id != relCaseAct.id)
2266 INNER JOIN civicrm_activity relAct ON (relCaseAct.activity_id = relAct.id AND relAct.activity_type_id = %1)
2267 WHERE $whereClause";
2268
2269 $dao = CRM_Core_DAO::executeQuery($query, array(
2270 1 => array($linkActType, 'Integer'),
2271 2 => array($mainCaseId, 'Integer'),
2272 ));
2273 $relatedCaseIds = array();
2274 while ($dao->fetch()) {
2275 $relatedCaseIds[$dao->case_id] = $dao->case_id;
2276 }
2277 $dao->free();
2278
2279 // there are no related cases.
2280 if (empty($relatedCaseIds)) {
2281 return $relatedCases;
2282 }
2283
2284 $whereClause = 'relCase.id IN ( ' . implode(',', $relatedCaseIds) . ' )';
2285 if ($excludeDeleted) {
2286 $whereClause .= " AND ( relCase.is_deleted = 0 OR relCase.is_deleted IS NULL )";
2287 }
2288
2289 //filter for permissioned cases.
2290 $filterCases = array();
2291 $doFilterCases = FALSE;
2292 if (!CRM_Core_Permission::check('access all cases and activities')) {
2293 $doFilterCases = TRUE;
e96f025a 2294 $session = CRM_Core_Session::singleton();
2295 $filterCases = CRM_Case_BAO_Case::getCases(FALSE, $session->get('userID'));
6a488035
TO
2296 }
2297
2298 //2. fetch the details of related cases.
2299 $query = "
2300 SELECT relCase.id as id,
8ffdec17 2301 civicrm_case_type.title as case_type,
6a488035
TO
2302 client.display_name as client_name,
2303 client.id as client_id
2304 FROM civicrm_case relCase
2305 INNER JOIN civicrm_case_contact relCaseContact ON ( relCase.id = relCaseContact.case_id )
2306 INNER JOIN civicrm_contact client ON ( client.id = relCaseContact.contact_id )
8ffdec17 2307 LEFT JOIN civicrm_case_type ON relCase.case_type_id = civicrm_case_type.id
6a488035
TO
2308 WHERE {$whereClause}";
2309
e96f025a 2310 $dao = CRM_Core_DAO::executeQuery($query);
6a488035
TO
2311 $contactViewUrl = CRM_Utils_System::url("civicrm/contact/view", "reset=1&cid=");
2312 $hasViewContact = CRM_Core_Permission::giveMeAllACLs();
2313
2314 while ($dao->fetch()) {
2315 $caseView = NULL;
2316 if (!$doFilterCases || array_key_exists($dao->id, $filterCases)) {
2317 $caseViewStr = "reset=1&id={$dao->id}&cid={$dao->client_id}&action=view&context=case&selectedChild=case";
2318 $caseViewUrl = CRM_Utils_System::url("civicrm/contact/view/case", $caseViewStr);
6ce08914 2319 $caseView = "<a class='action-item no-popup crm-hover-button' href='{$caseViewUrl}'>" . ts('View Case') . "</a>";
6a488035
TO
2320 }
2321 $clientView = $dao->client_name;
2322 if ($hasViewContact) {
2323 $clientView = "<a href='{$contactViewUrl}{$dao->client_id}'>$dao->client_name</a>";
2324 }
2325
2326 $relatedCases[$dao->id] = array(
2327 'case_id' => $dao->id,
2328 'case_type' => $dao->case_type,
2329 'client_name' => $clientView,
2330 'links' => $caseView,
2331 );
2332 }
2333 $dao->free();
2334
2335 return $relatedCases;
2336 }
2337
2338 /**
2339 * Merge two duplicate contacts' cases - follow CRM-5758 rules.
2340 *
2341 * @see CRM_Dedupe_Merger::cpTables()
2342 *
2343 * TODO: use the 3rd $sqls param to append sql statements rather than executing them here
cde2037d
EM
2344 *
2345 * @param int $mainContactId
2346 * @param int $otherContactId
6a488035 2347 */
00be9182 2348 public static function mergeContacts($mainContactId, $otherContactId) {
6a488035
TO
2349 self::mergeCases($mainContactId, NULL, $otherContactId);
2350 }
2351
2352 /**
2353 * Function perform two task.
2354 * 1. Merge two duplicate contacts cases - follow CRM-5758 rules.
2355 * 2. Merge two cases of same contact - follow CRM-5598 rules.
2356 *
64bd5a0e
TO
2357 * @param int $mainContactId
2358 * Contact id of main contact record.
2359 * @param int $mainCaseId
2360 * Case id of main case record.
2361 * @param int $otherContactId
2362 * Contact id of record which is going to merge.
2363 * @param int $otherCaseId
2364 * Case id of record which is going to merge.
77b97be7
EM
2365 *
2366 * @param bool $changeClient
6a488035 2367 *
df8d3074 2368 * @return int|NULL
6a488035 2369 */
a130e045 2370 public static function mergeCases(
28d4d481
TO
2371 $mainContactId, $mainCaseId = NULL, $otherContactId = NULL,
2372 $otherCaseId = NULL, $changeClient = FALSE) {
6a488035
TO
2373 $moveToTrash = TRUE;
2374
2375 $duplicateContacts = FALSE;
2376 if ($mainContactId && $otherContactId &&
2377 $mainContactId != $otherContactId
2378 ) {
2379 $duplicateContacts = TRUE;
2380 }
2381
2382 $duplicateCases = FALSE;
2383 if ($mainCaseId && $otherCaseId &&
2384 $mainCaseId != $otherCaseId
2385 ) {
2386 $duplicateCases = TRUE;
2387 }
2388
2389 $mainCaseIds = array();
2390 if (!$duplicateContacts && !$duplicateCases) {
2391 return $mainCaseIds;
2392 }
2393
2394 $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name');
2395 $activityStatuses = CRM_Core_PseudoConstant::activityStatus('name');
e7e657f0 2396 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
9e74e3ce 2397 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
2398 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
2399 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
8ef12e64 2400
6a488035
TO
2401 $processCaseIds = array($otherCaseId);
2402 if ($duplicateContacts && !$duplicateCases) {
2403 if ($changeClient) {
2404 $processCaseIds = array($mainCaseId);
2405 }
2406 else {
2407 //get all case ids for other contact.
2408 $processCaseIds = self::retrieveCaseIdsByContactId($otherContactId, TRUE);
2409 }
2410 if (!is_array($processCaseIds)) {
2411 return;
2412 }
2413 }
2414
2415 $session = CRM_Core_Session::singleton();
2416 $currentUserId = $session->get('userID');
2417
02094cdb
JJ
2418 CRM_Utils_Hook::pre_case_merge($mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient);
2419
6a488035
TO
2420 // copy all cases and connect to main contact id.
2421 foreach ($processCaseIds as $otherCaseId) {
2422 if ($duplicateContacts) {
2423 $mainCase = CRM_Core_DAO::copyGeneric('CRM_Case_DAO_Case', array('id' => $otherCaseId));
2424 $mainCaseId = $mainCase->id;
2425 if (!$mainCaseId) {
2426 continue;
2427 }
8bd86283
DG
2428
2429 // CRM-11662 Copy Case custom data
2430 $extends = array('case');
2431 $groupTree = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, NULL, $extends);
2432 if ($groupTree) {
2433 foreach ($groupTree as $groupID => $group) {
2434 $table[$groupTree[$groupID]['table_name']] = array('entity_id');
2435 foreach ($group['fields'] as $fieldID => $field) {
2436 $table[$groupTree[$groupID]['table_name']][] = $groupTree[$groupID]['fields'][$fieldID]['column_name'];
2437 }
2438 }
2439
2440 foreach ($table as $tableName => $tableColumns) {
e96f025a 2441 $insert = 'INSERT INTO ' . $tableName . ' (' . implode(', ', $tableColumns) . ') ';
8bd86283 2442 $tableColumns[0] = $mainCaseId;
e96f025a 2443 $select = 'SELECT ' . implode(', ', $tableColumns);
2444 $from = ' FROM ' . $tableName;
2445 $where = " WHERE {$tableName}.entity_id = {$otherCaseId}";
2446 $query = $insert . $select . $from . $where;
2447 $dao = CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
8bd86283
DG
2448 }
2449 }
e96f025a 2450
6a488035 2451 $mainCase->free();
e96f025a 2452
6a488035
TO
2453 $mainCaseIds[] = $mainCaseId;
2454 //insert record for case contact.
2455 $otherCaseContact = new CRM_Case_DAO_CaseContact();
2456 $otherCaseContact->case_id = $otherCaseId;
2457 $otherCaseContact->find();
2458 while ($otherCaseContact->fetch()) {
2459 $mainCaseContact = new CRM_Case_DAO_CaseContact();
2460 $mainCaseContact->case_id = $mainCaseId;
2461 $mainCaseContact->contact_id = $otherCaseContact->contact_id;
2462 if ($mainCaseContact->contact_id == $otherContactId) {
2463 $mainCaseContact->contact_id = $mainContactId;
2464 }
2465 //avoid duplicate object.
2466 if (!$mainCaseContact->find(TRUE)) {
2467 $mainCaseContact->save();
2468 }
2469 $mainCaseContact->free();
2470 }
2471 $otherCaseContact->free();
2472 }
2473 elseif (!$otherContactId) {
2474 $otherContactId = $mainContactId;
2475 }
2476
2477 if (!$mainCaseId || !$otherCaseId ||
2478 !$mainContactId || !$otherContactId
2479 ) {
2480 continue;
2481 }
2482
2483 // get all activities for other case.
2484 $otherCaseActivities = array();
2485 CRM_Core_DAO::commonRetrieveAll('CRM_Case_DAO_CaseActivity', 'case_id', $otherCaseId, $otherCaseActivities);
2486
2487 //for duplicate cases do not process singleton activities.
2488 $otherActivityIds = $singletonActivityIds = array();
2489 foreach ($otherCaseActivities as $caseActivityId => $otherIds) {
2490 $otherActId = CRM_Utils_Array::value('activity_id', $otherIds);
2491 if (!$otherActId || in_array($otherActId, $otherActivityIds)) {
2492 continue;
2493 }
2494 $otherActivityIds[] = $otherActId;
2495 }
2496 if ($duplicateCases) {
2497 if ($openCaseType = array_search('Open Case', $activityTypes)) {
2498 $sql = "
2499SELECT id
2500 FROM civicrm_activity
2501 WHERE activity_type_id = $openCaseType
2502 AND id IN ( " . implode(',', array_values($otherActivityIds)) . ');';
2503 $dao = CRM_Core_DAO::executeQuery($sql);
2504 while ($dao->fetch()) {
2505 $singletonActivityIds[] = $dao->id;
2506 }
2507 $dao->free();
2508 }
2509 }
2510
2511 // migrate all activities and connect to main contact.
2512 $copiedActivityIds = $activityMappingIds = array();
2513 sort($otherActivityIds);
2514 foreach ($otherActivityIds as $otherActivityId) {
2515
2516 //for duplicate cases -
2517 //do not migrate singleton activities.
2518 if (!$otherActivityId || in_array($otherActivityId, $singletonActivityIds)) {
2519 continue;
2520 }
2521
2522 //migrate activity record.
2523 $otherActivity = new CRM_Activity_DAO_Activity();
2524 $otherActivity->id = $otherActivityId;
2525 if (!$otherActivity->find(TRUE)) {
2526 continue;
2527 }
2528
2529 $mainActVals = array();
2530 $mainActivity = new CRM_Activity_DAO_Activity();
2531 CRM_Core_DAO::storeValues($otherActivity, $mainActVals);
2532 $mainActivity->copyValues($mainActVals);
2533 $mainActivity->id = NULL;
2534 $mainActivity->activity_date_time = CRM_Utils_Date::isoToMysql($otherActivity->activity_date_time);
6a488035
TO
2535 $mainActivity->source_record_id = CRM_Utils_Array::value($mainActivity->source_record_id,
2536 $activityMappingIds
2537 );
2538
2539 $mainActivity->original_id = CRM_Utils_Array::value($mainActivity->original_id,
2540 $activityMappingIds
2541 );
2542
2543 $mainActivity->parent_id = CRM_Utils_Array::value($mainActivity->parent_id,
2544 $activityMappingIds
2545 );
2546 $mainActivity->save();
2547 $mainActivityId = $mainActivity->id;
2548 if (!$mainActivityId) {
2549 continue;
2550 }
2551
2552 $activityMappingIds[$otherActivityId] = $mainActivityId;
4322672b 2553 // insert log of all activities
6a488035
TO
2554 CRM_Activity_BAO_Activity::logActivityAction($mainActivity);
2555
2556 $otherActivity->free();
2557 $mainActivity->free();
2558 $copiedActivityIds[] = $otherActivityId;
2559
2560 //create case activity record.
2561 $mainCaseActivity = new CRM_Case_DAO_CaseActivity();
2562 $mainCaseActivity->case_id = $mainCaseId;
2563 $mainCaseActivity->activity_id = $mainActivityId;
2564 $mainCaseActivity->save();
2565 $mainCaseActivity->free();
2566
4322672b 2567 //migrate source activity.
2568 $otherSourceActivity = new CRM_Activity_DAO_ActivityContact();
2569 $otherSourceActivity->activity_id = $otherActivityId;
2570 $otherSourceActivity->record_type_id = $sourceID;
2571 $otherSourceActivity->find();
2572 while ($otherSourceActivity->fetch()) {
2573 $mainActivitySource = new CRM_Activity_DAO_ActivityContact();
2574 $mainActivitySource->record_type_id = $sourceID;
2575 $mainActivitySource->activity_id = $mainActivityId;
2576 $mainActivitySource->contact_id = $otherSourceActivity->contact_id;
2577 if ($mainActivitySource->contact_id == $otherContactId) {
2578 $mainActivitySource->contact_id = $mainContactId;
2579 }
2580 //avoid duplicate object.
2581 if (!$mainActivitySource->find(TRUE)) {
2582 $mainActivitySource->save();
2583 }
2584 $mainActivitySource->free();
2585 }
2586 $otherSourceActivity->free();
2587
6a488035 2588 //migrate target activities.
4e3d3cfc 2589 $otherTargetActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2590 $otherTargetActivity->activity_id = $otherActivityId;
9e74e3ce 2591 $otherTargetActivity->record_type_id = $targetID;
6a488035
TO
2592 $otherTargetActivity->find();
2593 while ($otherTargetActivity->fetch()) {
4e3d3cfc 2594 $mainActivityTarget = new CRM_Activity_DAO_ActivityContact();
9e74e3ce 2595 $mainActivityTarget->record_type_id = $targetID;
6a488035 2596 $mainActivityTarget->activity_id = $mainActivityId;
00bf7e59 2597 $mainActivityTarget->contact_id = $otherTargetActivity->contact_id;
2598 if ($mainActivityTarget->contact_id == $otherContactId) {
2599 $mainActivityTarget->contact_id = $mainContactId;
6a488035
TO
2600 }
2601 //avoid duplicate object.
2602 if (!$mainActivityTarget->find(TRUE)) {
2603 $mainActivityTarget->save();
2604 }
2605 $mainActivityTarget->free();
2606 }
2607 $otherTargetActivity->free();
2608
2609 //migrate assignee activities.
4e3d3cfc 2610 $otherAssigneeActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2611 $otherAssigneeActivity->activity_id = $otherActivityId;
9e74e3ce 2612 $otherAssigneeActivity->record_type_id = $assigneeID;
6a488035
TO
2613 $otherAssigneeActivity->find();
2614 while ($otherAssigneeActivity->fetch()) {
4e3d3cfc 2615 $mainAssigneeActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2616 $mainAssigneeActivity->activity_id = $mainActivityId;
9e74e3ce 2617 $mainAssigneeActivity->record_type_id = $assigneeID;
00bf7e59 2618 $mainAssigneeActivity->contact_id = $otherAssigneeActivity->contact_id;
2619 if ($mainAssigneeActivity->contact_id == $otherContactId) {
2620 $mainAssigneeActivity->contact_id = $mainContactId;
6a488035
TO
2621 }
2622 //avoid duplicate object.
2623 if (!$mainAssigneeActivity->find(TRUE)) {
2624 $mainAssigneeActivity->save();
2625 }
2626 $mainAssigneeActivity->free();
2627 }
2628 $otherAssigneeActivity->free();
8c31eef5
D
2629
2630 // copy custom fields and attachments
4322672b 2631 $aparams = array(
2632 'activityID' => $otherActivityId,
2633 'mainActivityId' => $mainActivityId,
2634 );
8c31eef5 2635 CRM_Activity_BAO_Activity::copyExtendedActivityData($aparams);
6a488035
TO
2636 }
2637
2638 //copy case relationship.
2639 if ($duplicateContacts) {
2640 //migrate relationship records.
2641 $otherRelationship = new CRM_Contact_DAO_Relationship();
2642 $otherRelationship->case_id = $otherCaseId;
2643 $otherRelationship->find();
2644 $otherRelationshipIds = array();
2645 while ($otherRelationship->fetch()) {
2646 $otherRelVals = array();
2647 $updateOtherRel = FALSE;
2648 CRM_Core_DAO::storeValues($otherRelationship, $otherRelVals);
2649
2650 $mainRelationship = new CRM_Contact_DAO_Relationship();
2651 $mainRelationship->copyValues($otherRelVals);
2652 $mainRelationship->id = NULL;
2653 $mainRelationship->case_id = $mainCaseId;
2654 if ($mainRelationship->contact_id_a == $otherContactId) {
2655 $updateOtherRel = TRUE;
2656 $mainRelationship->contact_id_a = $mainContactId;
2657 }
2658
2659 //case creator change only when we merge user contact.
2660 if ($mainRelationship->contact_id_b == $otherContactId) {
2661 //do not change creator for change client.
2662 if (!$changeClient) {
2663 $updateOtherRel = TRUE;
2664 $mainRelationship->contact_id_b = ($currentUserId) ? $currentUserId : $mainContactId;
2665 }
2666 }
2667 $mainRelationship->end_date = CRM_Utils_Date::isoToMysql($otherRelationship->end_date);
2668 $mainRelationship->start_date = CRM_Utils_Date::isoToMysql($otherRelationship->start_date);
2669
2670 //avoid duplicate object.
2671 if (!$mainRelationship->find(TRUE)) {
2672 $mainRelationship->save();
2673 }
2674 $mainRelationship->free();
2675
2676 //get the other relationship ids to update end date.
2677 if ($updateOtherRel) {
2678 $otherRelationshipIds[$otherRelationship->id] = $otherRelationship->id;
2679 }
2680 }
2681 $otherRelationship->free();
2682
2683 //update other relationships end dates
2684 if (!empty($otherRelationshipIds)) {
2685 $sql = 'UPDATE civicrm_relationship
2686 SET end_date = CURDATE()
2687 WHERE id IN ( ' . implode(',', $otherRelationshipIds) . ')';
2688 CRM_Core_DAO::executeQuery($sql);
2689 }
2690 }
2691
2692 //move other case to trash.
2693 $mergeCase = self::deleteCase($otherCaseId, $moveToTrash);
2694 if (!$mergeCase) {
2695 continue;
2696 }
2697
2698 $mergeActSubject = $mergeActSubjectDetails = $mergeActType = '';
2699 if ($changeClient) {
2700 $mainContactDisplayName = CRM_Contact_BAO_Contact::displayName($mainContactId);
2701 $otherContactDisplayName = CRM_Contact_BAO_Contact::displayName($otherContactId);
2702
2703 $mergeActType = array_search('Reassigned Case', $activityTypes);
2704 $mergeActSubject = ts("Case %1 reassigned client from %2 to %3. New Case ID is %4.",
2705 array(
e96f025a 2706 1 => $otherCaseId,
2707 2 => $otherContactDisplayName,
2708 3 => $mainContactDisplayName,
21dfd5f5 2709 4 => $mainCaseId,
6a488035
TO
2710 )
2711 );
2712 }
2713 elseif ($duplicateContacts) {
2714 $mergeActType = array_search('Merge Case', $activityTypes);
2715 $mergeActSubject = ts("Case %1 copied from contact id %2 to contact id %3 via merge. New Case ID is %4.",
2716 array(
e96f025a 2717 1 => $otherCaseId,
2718 2 => $otherContactId,
2719 3 => $mainContactId,
21dfd5f5 2720 4 => $mainCaseId,
6a488035
TO
2721 )
2722 );
2723 }
2724 else {
2725 $mergeActType = array_search('Merge Case', $activityTypes);
2726 $mergeActSubject = ts("Case %1 merged into case %2", array(1 => $otherCaseId, 2 => $mainCaseId));
2727 if (!empty($copiedActivityIds)) {
2728 $sql = '
2729SELECT id, subject, activity_date_time, activity_type_id
2730FROM civicrm_activity
2731WHERE id IN (' . implode(',', $copiedActivityIds) . ')';
2732 $dao = CRM_Core_DAO::executeQuery($sql);
2733 while ($dao->fetch()) {
2734 $mergeActSubjectDetails .= "{$dao->activity_date_time} :: {$activityTypes[$dao->activity_type_id]}";
2735 if ($dao->subject) {
2736 $mergeActSubjectDetails .= " :: {$dao->subject}";
2737 }
2738 $mergeActSubjectDetails .= "<br />";
2739 }
2740 }
2741 }
2742
8c677b07 2743 //Create merge activity record. Source for merge activity is the logged in user's contact ID ($currentUserId).
6a488035
TO
2744 $activityParams = array(
2745 'subject' => $mergeActSubject,
2746 'details' => $mergeActSubjectDetails,
2747 'status_id' => array_search('Completed', $activityStatuses),
2748 'activity_type_id' => $mergeActType,
8c677b07 2749 'source_contact_id' => $currentUserId,
6a488035
TO
2750 'activity_date_time' => date('YmdHis'),
2751 );
2752
2753 $mergeActivity = CRM_Activity_BAO_Activity::create($activityParams);
2754 $mergeActivityId = $mergeActivity->id;
2755 if (!$mergeActivityId) {
2756 continue;
2757 }
2758 $mergeActivity->free();
2759
2760 //connect merge activity to case.
2761 $mergeCaseAct = array(
2762 'case_id' => $mainCaseId,
2763 'activity_id' => $mergeActivityId,
2764 );
2765
2766 self::processCaseActivity($mergeCaseAct);
2767 }
02094cdb
JJ
2768
2769 CRM_Utils_Hook::post_case_merge($mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient);
2770
6a488035
TO
2771 return $mainCaseIds;
2772 }
2773
2774 /**
2775 * Validate contact permission for
2776 * edit/view on activity record and build links.
2777 *
64bd5a0e
TO
2778 * @param array $tplParams
2779 * Params to be sent to template for sending email.
2780 * @param array $activityParams
2781 * Info of the activity.
6a488035 2782 */
00be9182 2783 public static function buildPermissionLinks(&$tplParams, $activityParams) {
6a488035
TO
2784 $activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityParams['source_record_id'],
2785 'activity_type_id', 'id'
2786 );
2787
a7488080 2788 if (!empty($tplParams['isCaseActivity'])) {
6a488035
TO
2789 $tplParams['editActURL'] = CRM_Utils_System::url('civicrm/case/activity',
2790 "reset=1&cid={$activityParams['target_id']}&caseid={$activityParams['case_id']}&action=update&id={$activityParams['source_record_id']}", TRUE
2791 );
2792
2793 $tplParams['viewActURL'] = CRM_Utils_System::url('civicrm/case/activity/view',
2794 "reset=1&aid={$activityParams['source_record_id']}&cid={$activityParams['target_id']}&caseID={$activityParams['case_id']}", TRUE
2795 );
2796
2797 $tplParams['manageCaseURL'] = CRM_Utils_System::url('civicrm/contact/view/case',
2798 "reset=1&id={$activityParams['case_id']}&cid={$activityParams['target_id']}&action=view&context=home", TRUE
2799 );
2800 }
2801 else {
2802 $tplParams['editActURL'] = CRM_Utils_System::url('civicrm/contact/view/activity',
2803 "atype=$activityTypeId&action=update&reset=1&id={$activityParams['source_record_id']}&cid={$tplParams['contact']['contact_id']}&context=activity", TRUE
2804 );
2805
2806 $tplParams['viewActURL'] = CRM_Utils_System::url('civicrm/contact/view/activity',
2807 "atype=$activityTypeId&action=view&reset=1&id={$activityParams['source_record_id']}&cid={$tplParams['contact']['contact_id']}&context=activity", TRUE
2808 );
2809 }
2810 }
2811
2812 /**
2813 * Validate contact permission for
2814 * given operation on activity record.
2815 *
64bd5a0e
TO
2816 * @param int $activityId
2817 * Activity record id.
2818 * @param string $operation
2819 * User operation.
2820 * @param int $actTypeId
2821 * Activity type id.
2822 * @param int $contactId
2823 * Contact id/if not pass consider logged in.
2824 * @param bool $checkComponent
2825 * Do we need to check component enabled.
6a488035 2826 *
a130e045 2827 * @return bool
6a488035 2828 */
00be9182 2829 public static function checkPermission($activityId, $operation, $actTypeId = NULL, $contactId = NULL, $checkComponent = TRUE) {
6a488035
TO
2830 $allow = FALSE;
2831 if (!$actTypeId && $activityId) {
2832 $actTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id');
2833 }
2834
2835 if (!$activityId || !$operation || !$actTypeId) {
2836 return $allow;
2837 }
2838
2839 //do check for civicase component enabled.
077dbf5e
CW
2840 if ($checkComponent && !self::enabled()) {
2841 return $allow;
6a488035
TO
2842 }
2843
2844 //do check for cases.
2845 $caseActOperations = array(
2846 'File On Case',
2847 'Link Cases',
2848 'Move To Case',
2849 'Copy To Case',
2850 );
2851
2852 if (in_array($operation, $caseActOperations)) {
2853 static $unclosedCases;
2854 if (!is_array($unclosedCases)) {
2855 $unclosedCases = self::getUnclosedCases();
2856 }
2857 if ($operation == 'File On Case') {
2858 $allow = (empty($unclosedCases)) ? FALSE : TRUE;
2859 }
2860 else {
2861 $allow = (count($unclosedCases) > 1) ? TRUE : FALSE;
2862 }
2863 }
2864
2865 $actionOperations = array('view', 'edit', 'delete');
2866 if (in_array($operation, $actionOperations)) {
2867
2868 //do cache when user has non/supper permission.
2869 static $allowOperations;
2870
2871 if (!is_array($allowOperations) ||
2872 !array_key_exists($operation, $allowOperations)
2873 ) {
2874
2875 if (!$contactId) {
2876 $session = CRM_Core_Session::singleton();
2877 $contactId = $session->get('userID');
2878 }
2879
2880 //check for permissions.
2881 $permissions = array(
2882 'view' => array(
2883 'access my cases and activities',
2884 'access all cases and activities',
2885 ),
2886 'edit' => array(
2887 'access my cases and activities',
2888 'access all cases and activities',
2889 ),
2890 'delete' => array('delete activities'),
2891 );
2892
2893 //check for core permission.
2894 $hasPermissions = array();
2895 $checkPermissions = CRM_Utils_Array::value($operation, $permissions);
2896 if (is_array($checkPermissions)) {
2897 foreach ($checkPermissions as $per) {
2898 if (CRM_Core_Permission::check($per)) {
2899 $hasPermissions[$operation][] = $per;
2900 }
2901 }
2902 }
2903
2904 //has permissions.
2905 if (!empty($hasPermissions)) {
2906 //need to check activity object specific.
2907 if (in_array($operation, array(
e96f025a 2908 'view',
21dfd5f5 2909 'edit',
e96f025a 2910 ))
2911 ) {
6a488035
TO
2912 //do we have supper permission.
2913 if (in_array('access all cases and activities', $hasPermissions[$operation])) {
2914 $allowOperations[$operation] = $allow = TRUE;
2915 }
2916 else {
2917 //user has only access to my cases and activity.
2918 //here object specific permmions come in picture.
2919
2920 //edit - contact must be source or assignee
2921 //view - contact must be source/assignee/target
2922 $isTarget = $isAssignee = $isSource = FALSE;
499e172c 2923 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
4322672b 2924 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
2925 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
2926 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
6a488035 2927
4e3d3cfc 2928 $target = new CRM_Activity_DAO_ActivityContact();
9e74e3ce 2929 $target->record_type_id = $targetID;
6a488035 2930 $target->activity_id = $activityId;
00bf7e59 2931 $target->contact_id = $contactId;
6a488035
TO
2932 if ($target->find(TRUE)) {
2933 $isTarget = TRUE;
2934 }
2935
4e3d3cfc 2936 $assignee = new CRM_Activity_DAO_ActivityContact();
6a488035 2937 $assignee->activity_id = $activityId;
9e74e3ce 2938 $assignee->record_type_id = $assigneeID;
00bf7e59 2939 $assignee->contact_id = $contactId;
6a488035
TO
2940 if ($assignee->find(TRUE)) {
2941 $isAssignee = TRUE;
2942 }
2943
4322672b 2944 $source = new CRM_Activity_DAO_ActivityContact();
2945 $source->activity_id = $activityId;
2946 $source->record_type_id = $sourceID;
2947 $source->contact_id = $contactId;
2948 if ($source->find(TRUE)) {
6a488035
TO
2949 $isSource = TRUE;
2950 }
2951
2952 if ($operation == 'edit') {
2953 if ($isAssignee || $isSource) {
2954 $allow = TRUE;
2955 }
2956 }
2957 if ($operation == 'view') {
2958 if ($isTarget || $isAssignee || $isSource) {
2959 $allow = TRUE;
2960 }
2961 }
2962 }
2963 }
2964 elseif (is_array($hasPermissions[$operation])) {
2965 $allowOperations[$operation] = $allow = TRUE;
2966 }
2967 }
2968 else {
2969 //contact do not have permission.
2970 $allowOperations[$operation] = FALSE;
2971 }
2972 }
2973 else {
2974 //use cache.
2975 //here contact might have supper/non permission.
2976 $allow = $allowOperations[$operation];
2977 }
2978 }
2979
2980 //do further only when operation is granted.
2981 if ($allow) {
2982 $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name');
2983
2984 //get the activity type name.
2985 $actTypeName = CRM_Utils_Array::value($actTypeId, $activityTypes);
2986
2987 //do not allow multiple copy / edit action.
e96f025a 2988 $singletonNames = array(
2989 'Open Case',
2990 'Reassigned Case',
2991 'Merge Case',
2992 'Link Cases',
2993 'Assign Case Role',
2994 'Email',
21dfd5f5 2995 'Inbound Email',
e96f025a 2996 );
6a488035
TO
2997
2998 //do not allow to delete these activities, CRM-4543
2999 $doNotDeleteNames = array('Open Case', 'Change Case Type', 'Change Case Status', 'Change Case Start Date');
3000
3001 //allow edit operation.
3002 $allowEditNames = array('Open Case');
3003
3004 // do not allow File on Case
e96f025a 3005 $doNotFileNames = array(
3006 'Open Case',
3007 'Change Case Type',
3008 'Change Case Status',
3009 'Change Case Start Date',
3010 'Reassigned Case',
3011 'Merge Case',
3012 'Link Cases',
21dfd5f5 3013 'Assign Case Role',
e96f025a 3014 );
6a488035
TO
3015
3016 if (in_array($actTypeName, $singletonNames)) {
3017 $allow = FALSE;
3018 if ($operation == 'File On Case') {
3019 $allow = (in_array($actTypeName, $doNotFileNames)) ? FALSE : TRUE;
3020 }
3021 if (in_array($operation, $actionOperations)) {
3022 $allow = TRUE;
3023 if ($operation == 'edit') {
3024 $allow = (in_array($actTypeName, $allowEditNames)) ? TRUE : FALSE;
3025 }
3026 elseif ($operation == 'delete') {
3027 $allow = (in_array($actTypeName, $doNotDeleteNames)) ? FALSE : TRUE;
3028 }
3029 }
3030 }
3031 if ($allow && ($operation == 'delete') &&
3032 in_array($actTypeName, $doNotDeleteNames)
3033 ) {
3034 $allow = FALSE;
3035 }
3036
3037 if ($allow && ($operation == 'File On Case') &&
3038 in_array($actTypeName, $doNotFileNames)
3039 ) {
3040 $allow = FALSE;
3041 }
3042
3043 //check settings file for masking actions
3044 //on the basis the activity types
3045 //hide Edit link if activity type is NOT editable
3046 //(special case activities).CRM-5871
3047 if ($allow && in_array($operation, $actionOperations)) {
3048 static $actionFilter = array();
3049 if (!array_key_exists($operation, $actionFilter)) {
3050 $xmlProcessor = new CRM_Case_XMLProcessor_Process();
3051 $actionFilter[$operation] = $xmlProcessor->get('Settings', 'ActivityTypes', FALSE, $operation);
3052 }
3053 if (array_key_exists($operation, $actionFilter[$operation]) &&
3054 in_array($actTypeId, $actionFilter[$operation][$operation])
3055 ) {
3056 $allow = FALSE;
3057 }
3058 }
3059 }
3060
3061 return $allow;
3062 }
3063
3064 /**
100fef9d 3065 * Since we drop 'access CiviCase', allow access
6a488035
TO
3066 * if user has 'access my cases and activities'
3067 * or 'access all cases and activities'
3068 */
00be9182 3069 public static function accessCiviCase() {
077dbf5e 3070 if (!self::enabled()) {
6a488035
TO
3071 return FALSE;
3072 }
3073
3074 if (CRM_Core_Permission::check('access my cases and activities') ||
3075 CRM_Core_Permission::check('access all cases and activities')
3076 ) {
3077 return TRUE;
3078 }
3079
3080 return FALSE;
3081 }
3082
3083 /**
d2e5d2ce 3084 * Verify user has permission to access a case.
077dbf5e
CW
3085 *
3086 * @param int $caseId
64bd5a0e
TO
3087 * @param bool $denyClosed
3088 * Set TRUE if one wants closed cases to be treated as inaccessible.
077dbf5e
CW
3089 *
3090 * @return bool
3091 */
00be9182 3092 public static function accessCase($caseId, $denyClosed = TRUE) {
077dbf5e
CW
3093 if (!$caseId || !self::enabled()) {
3094 return FALSE;
3095 }
3096
3097 // This permission always has access
3098 if (CRM_Core_Permission::check('access all cases and activities')) {
3099 return TRUE;
3100 }
3101
3102 // This permission is required at minimum
3103 if (!CRM_Core_Permission::check('access my cases and activities')) {
3104 return FALSE;
3105 }
3106
546abeb1
TO
3107 $session = CRM_Core_Session::singleton();
3108 $userID = CRM_Utils_Type::validate($session->get('userID'), 'Positive');
3109 $caseId = CRM_Utils_Type::validate($caseId, 'Positive');
3110
3111 $condition = " AND civicrm_case.is_deleted = 0 ";
3112 $condition .= " AND case_relationship.contact_id_b = {$userID} ";
3113 $condition .= " AND civicrm_case.id = {$caseId}";
3114
3115 if ($denyClosed) {
3116 $closedId = CRM_Core_OptionGroup::getValue('case_status', 'Closed', 'name');
3117 $condition .= " AND civicrm_case.status_id != $closedId";
3118 }
3119
3120 // We don't actually care about activities in the case, but the underlying
3121 // query is verbose, and this allows us to share the basic query with
3122 // getCases(). $type=='any' means that activities will be left-joined.
3123 $query = self::getCaseActivityQuery('any', $userID, $condition);
3124 $queryParams = array();
3125 $dao = CRM_Core_DAO::executeQuery($query,
3126 $queryParams
3127 );
077dbf5e 3128
546abeb1 3129 return (bool) $dao->fetch();
077dbf5e
CW
3130 }
3131
6a488035 3132 /**
d2e5d2ce 3133 * Check whether activity is a case Activity.
6a488035 3134 *
64bd5a0e
TO
3135 * @param int $activityID
3136 * Activity id.
6a488035 3137 *
a130e045 3138 * @return bool
6a488035 3139 */
00be9182 3140 public static function isCaseActivity($activityID) {
6a488035
TO
3141 $isCaseActivity = FALSE;
3142 if ($activityID) {
3143 $params = array(1 => array($activityID, 'Integer'));
3144 $query = "SELECT id FROM civicrm_case_activity WHERE activity_id = %1";
3145 if (CRM_Core_DAO::singleValueQuery($query, $params)) {
3146 $isCaseActivity = TRUE;
3147 }
3148 }
3149
3150 return $isCaseActivity;
3151 }
3152
3153 /**
d2e5d2ce 3154 * Get all the case type ids currently in use.
6a488035 3155 *
a6c01b45 3156 * @return array
6a488035 3157 */
00be9182 3158 public static function getUsedCaseType() {
6a488035
TO
3159 static $caseTypeIds;
3160
3161 if (!is_array($caseTypeIds)) {
3162 $query = "SELECT DISTINCT( civicrm_case.case_type_id ) FROM civicrm_case";
3163
3164 $dao = CRM_Core_DAO::executeQuery($query);
3165 $caseTypeIds = array();
3166 while ($dao->fetch()) {
3167 $typeId = explode(CRM_Core_DAO::VALUE_SEPARATOR,
3168 $dao->case_type_id
3169 );
3170 $caseTypeIds[] = $typeId[1];
3171 }
3172 }
3173
3174 return $caseTypeIds;
3175 }
3176
3177 /**
d2e5d2ce 3178 * Get all the case status ids currently in use.
6a488035 3179 *
a6c01b45 3180 * @return array
6a488035 3181 */
00be9182 3182 public static function getUsedCaseStatuses() {
6a488035
TO
3183 static $caseStatusIds;
3184
3185 if (!is_array($caseStatusIds)) {
3186 $query = "SELECT DISTINCT( civicrm_case.status_id ) FROM civicrm_case";
3187
3188 $dao = CRM_Core_DAO::executeQuery($query);
3189 $caseStatusIds = array();
3190 while ($dao->fetch()) {
3191 $caseStatusIds[] = $dao->status_id;
3192 }
3193 }
3194
3195 return $caseStatusIds;
3196 }
3197
3198 /**
d2e5d2ce 3199 * Get all the encounter medium ids currently in use.
72b3a70c 3200 *
6a488035
TO
3201 * @return array
3202 */
00be9182 3203 public static function getUsedEncounterMediums() {
6a488035
TO
3204 static $mediumIds;
3205
3206 if (!is_array($mediumIds)) {
3207 $query = "SELECT DISTINCT( civicrm_activity.medium_id ) FROM civicrm_activity";
3208
3209 $dao = CRM_Core_DAO::executeQuery($query);
3210 $mediumIds = array();
3211 while ($dao->fetch()) {
3212 $mediumIds[] = $dao->medium_id;
3213 }
3214 }
3215
3216 return $mediumIds;
3217 }
3218
3219 /**
100fef9d 3220 * Check case configuration.
6a488035 3221 *
100fef9d 3222 * @param int $contactId
77b97be7 3223 *
a6c01b45 3224 * @return array
6a488035 3225 */
00be9182 3226 public static function isCaseConfigured($contactId = NULL) {
6a488035
TO
3227 $configured = array_fill_keys(array('configured', 'allowToAddNewCase', 'redirectToCaseAdmin'), FALSE);
3228
3229 //lets check for case configured.
3230 $allCasesCount = CRM_Case_BAO_Case::caseCount(NULL, FALSE);
3231 $configured['configured'] = ($allCasesCount) ? TRUE : FALSE;
3232 if (!$configured['configured']) {
3233 //do check for case type and case status.
0372ffa2 3234 $caseTypes = CRM_Case_PseudoConstant::caseType('title', FALSE);
6a488035
TO
3235 if (!empty($caseTypes)) {
3236 $configured['configured'] = TRUE;
3237 if (!$configured['configured']) {
3238 $caseStatuses = CRM_Case_PseudoConstant::caseStatus('label', FALSE);
3239 if (!empty($caseStatuses)) {
3240 $configured['configured'] = TRUE;
3241 }
3242 }
3243 }
3244 }
3245 if ($configured['configured']) {
3246 //do check for active case type and case status.
3247 $caseTypes = CRM_Case_PseudoConstant::caseType();
3248 if (!empty($caseTypes)) {
3249 $caseStatuses = CRM_Case_PseudoConstant::caseStatus();
3250 if (!empty($caseStatuses)) {
3251 $configured['allowToAddNewCase'] = TRUE;
3252 }
3253 }
3254
3255 //do we need to redirect user to case admin.
3256 if (!$configured['allowToAddNewCase'] && $contactId) {
3257 //check for current contact case count.
3258 $currentContatCasesCount = CRM_Case_BAO_Case::caseCount($contactId);
3259 //redirect user to case admin page.
3260 if (!$currentContatCasesCount) {
3261 $configured['redirectToCaseAdmin'] = TRUE;
3262 }
3263 }
3264 }
3265
3266 return $configured;
3267 }
3268
d6f468d3 3269 /**
d2e5d2ce 3270 * Used during case component enablement and during ugprade.
72b3a70c
CW
3271 *
3272 * @return bool
6a488035 3273 */
00be9182 3274 public static function createCaseViews() {
6a4257d4 3275 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
a19fc402
TO
3276 $dao = new CRM_Core_DAO();
3277
6a488035 3278 $sql = self::createCaseViewsQuery('upcoming');
6a488035
TO
3279 $dao->query($sql);
3280 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
3281 return FALSE;
3282 }
3283
3284 // Above error doesn't get caught?
3285 $doublecheck = $dao->singleValueQuery("SELECT count(id) FROM civicrm_view_case_activity_upcoming");
3286 if (is_null($doublecheck)) {
3287 return FALSE;
3288 }
3289
3290 $sql = self::createCaseViewsQuery('recent');
6a488035
TO
3291 $dao->query($sql);
3292 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
3293 return FALSE;
3294 }
3295
3296 // Above error doesn't get caught?
3297 $doublecheck = $dao->singleValueQuery("SELECT count(id) FROM civicrm_view_case_activity_recent");
3298 if (is_null($doublecheck)) {
3299 return FALSE;
3300 }
3301
3302 return TRUE;
3303 }
3304
d6f468d3 3305 /**
100fef9d 3306 * Helper function, also used by the upgrade in case of error
72b3a70c 3307 *
cde2037d
EM
3308 * @param string $section
3309 *
72b3a70c 3310 * @return string
6a488035 3311 */
00be9182 3312 public static function createCaseViewsQuery($section = 'upcoming') {
6a488035
TO
3313 $sql = "";
3314 $scheduled_id = CRM_Core_OptionGroup::getValue('activity_status', 'Scheduled', 'name');
3315 switch ($section) {
3316 case 'upcoming':
3317 $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_upcoming`
3318 AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
3319 FROM civicrm_case_activity ca
3320 INNER JOIN civicrm_activity a ON ca.activity_id=a.id
3321 WHERE a.activity_date_time <= DATE_ADD( NOW(), INTERVAL 14 DAY )
3322 AND a.is_current_revision = 1 AND a.is_deleted=0 AND a.status_id = $scheduled_id";
3323 break;
3324
3325 case 'recent':
3326 $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_recent`
3327 AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
3328 FROM civicrm_case_activity ca
3329 INNER JOIN civicrm_activity a ON ca.activity_id=a.id
3330 WHERE a.activity_date_time <= NOW()
3331 AND a.activity_date_time >= DATE_SUB( NOW(), INTERVAL 14 DAY )
3332 AND a.is_current_revision = 1 AND a.is_deleted=0 AND a.status_id <> $scheduled_id";
3333 break;
3334 }
6a488035 3335 return $sql;
e96f025a 3336 }
3337
3338 /**
100fef9d 3339 * Add/copy relationships, when new client is added for a case
e96f025a 3340 *
64bd5a0e
TO
3341 * @param int $caseId
3342 * Case id.
3343 * @param int $contactId
3344 * Contact id / new client id.
e96f025a 3345 */
00be9182 3346 public static function addCaseRelationships($caseId, $contactId) {
44466d80
KJ
3347 // get the case role / relationships for the case
3348 $caseRelationships = new CRM_Contact_DAO_Relationship();
3349 $caseRelationships->case_id = $caseId;
3350 $caseRelationships->find();
3351 $relationshipTypes = array();
3352
3353 // make sure we don't add duplicate relationships of same relationship type.
3354 while ($caseRelationships->fetch() && !in_array($caseRelationships->relationship_type_id, $relationshipTypes)) {
3355 $values = array();
3356 CRM_Core_DAO::storeValues($caseRelationships, $values);
3357
3358 // add relationship for new client.
3359 $newRelationship = new CRM_Contact_DAO_Relationship();
3360 $newRelationship->copyValues($values);
3361 $newRelationship->id = NULL;
3362 $newRelationship->case_id = $caseId;
3363 $newRelationship->contact_id_a = $contactId;
3364 $newRelationship->end_date = CRM_Utils_Date::isoToMysql($caseRelationships->end_date);
3365 $newRelationship->start_date = CRM_Utils_Date::isoToMysql($caseRelationships->start_date);
3366
3367 // another check to avoid duplicate relationship, in cases where client is removed and re-added again.
3368 if (!$newRelationship->find(TRUE)) {
3369 $newRelationship->save();
3370 }
3371 $newRelationship->free();
3372
3373 // store relationship type of newly created relationship
3374 $relationshipTypes[] = $caseRelationships->relationship_type_id;
3375 }
6a488035 3376 }
14a679f1
KJ
3377
3378 /**
d2e5d2ce 3379 * Get the list of clients for a case.
14a679f1
KJ
3380 *
3381 * @param int $caseId
3382 *
a6c01b45
CW
3383 * @return array
3384 * associated array with client ids
14a679f1 3385 */
00be9182 3386 public static function getCaseClients($caseId) {
14a679f1
KJ
3387 $clients = array();
3388 $caseContact = new CRM_Case_DAO_CaseContact();
3389 $caseContact->case_id = $caseId;
3390 $caseContact->find();
3391
e96f025a 3392 while ($caseContact->fetch()) {
14a679f1
KJ
3393 $clients[] = $caseContact->contact_id;
3394 }
3395
3396 return $clients;
3397 }
16c0ec8d 3398
3b1c37fe
CW
3399 /**
3400 * @param int $caseId
3401 * @param string $direction
3402 * @param int $cid
3403 * @param int $relTypeId
3404 * @throws \CRM_Core_Exception
3405 * @throws \CiviCRM_API3_Exception
3406 */
3407 public static function endCaseRole($caseId, $direction, $cid, $relTypeId) {
3408 // Validate inputs
3409 if ($direction !== 'a' && $direction !== 'b') {
3410 throw new CRM_Core_Exception('Invalid relationship direction');
3411 }
3412
3413 // This case might have multiple clients, so we lookup by relationship instead of by id to get them all
3414 $sql = "SELECT id FROM civicrm_relationship WHERE case_id = %1 AND contact_id_{$direction} = %2 AND relationship_type_id = %3";
3415 $dao = CRM_Core_DAO::executeQuery($sql, array(
3416 1 => array($caseId, 'Positive'),
3417 2 => array($cid, 'Positive'),
3418 3 => array($relTypeId, 'Positive'),
3419 ));
3420 while ($dao->fetch()) {
3421 civicrm_api3('relationship', 'create', array(
3422 'id' => $dao->id,
3423 'is_active' => 0,
3424 'end_date' => 'now',
3425 ));
3426 }
3427 }
3428
16c0ec8d
CW
3429 /**
3430 * Get options for a given case field.
3431 * @see CRM_Core_DAO::buildOptions
3432 *
64bd5a0e
TO
3433 * @param string $fieldName
3434 * @param string $context
408b79bf 3435 * @see CRM_Core_DAO::buildOptionsContext
64bd5a0e 3436 * @param array $props
72b3a70c 3437 * Whatever is known about this dao object.
77b97be7 3438 *
a130e045 3439 * @return array|bool
16c0ec8d
CW
3440 */
3441 public static function buildOptions($fieldName, $context = NULL, $props = array()) {
3442 $className = __CLASS__;
3443 $params = array();
3444 switch ($fieldName) {
3445 // This field is not part of this object but the api supports it
3446 case 'medium_id':
3447 $className = 'CRM_Activity_BAO_Activity';
3448 break;
3449 }
3450 return CRM_Core_PseudoConstant::get($className, $fieldName, $params, $context);
3451 }
96025800 3452
6a488035 3453}