CRM-16980 - Only allow contacts of same type to be added as clients
[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
TO
970 $query = '
971 SELECT civicrm_relationship.id as civicrm_relationship_id,
972 civicrm_contact.sort_name as sort_name,
973 civicrm_email.email as email,
974 civicrm_phone.phone as phone,
975 civicrm_relationship.contact_id_b as civicrm_contact_id,
976 civicrm_relationship.contact_id_a as client_id,
977 civicrm_relationship_type.label_a_b as relation,
978 civicrm_relationship_type.id as relation_type
979 FROM civicrm_relationship
980 INNER JOIN civicrm_relationship_type ON civicrm_relationship.relationship_type_id = civicrm_relationship_type.id
981 INNER JOIN civicrm_contact ON civicrm_relationship.contact_id_b = civicrm_contact.id
982 LEFT JOIN civicrm_phone ON (civicrm_phone.contact_id = civicrm_contact.id AND civicrm_phone.is_primary = 1)
7f3c40d8 983 LEFT JOIN civicrm_email ON (civicrm_email.contact_id = civicrm_contact.id AND civicrm_email.is_primary = 1)
6a488035
TO
984 WHERE civicrm_relationship.contact_id_a = %1 AND civicrm_relationship.case_id = %2';
985
6a488035
TO
986 $params = array(
987 1 => array($contactID, 'Positive'),
988 2 => array($caseID, 'Positive'),
989 );
990
991 if ($relationshipID) {
992 $query .= ' AND civicrm_relationship.id = %3 ';
993 $params[3] = array($relationshipID, 'Integer');
994 }
995 $dao = CRM_Core_DAO::executeQuery($query, $params);
996
997 $values = array();
998 while ($dao->fetch()) {
999 $rid = $dao->civicrm_relationship_id;
1000 $values[$rid]['cid'] = $dao->civicrm_contact_id;
1001 $values[$rid]['relation'] = $dao->relation;
1002 $values[$rid]['name'] = $dao->sort_name;
1003 $values[$rid]['email'] = $dao->email;
1004 $values[$rid]['phone'] = $dao->phone;
1005 $values[$rid]['relation_type'] = $dao->relation_type;
1006 $values[$rid]['rel_id'] = $dao->civicrm_relationship_id;
1007 $values[$rid]['client_id'] = $dao->client_id;
1008 }
1009
1010 $dao->free();
1011 return $values;
1012 }
1013
1014 /**
d2e5d2ce 1015 * Get Case Activities.
6a488035 1016 *
64bd5a0e
TO
1017 * @param int $caseID
1018 * Case id.
1019 * @param array $params
1020 * Posted params.
1021 * @param int $contactID
1022 * Contact id.
6a488035 1023 *
77b97be7 1024 * @param null $context
100fef9d 1025 * @param int $userID
77b97be7
EM
1026 * @param null $type
1027 *
a6c01b45 1028 * @return array
16b10e64 1029 * Array of case activities
6a488035 1030 *
6a488035 1031 */
00be9182 1032 public static function getCaseActivity($caseID, &$params, $contactID, $context = NULL, $userID = NULL, $type = NULL) {
6a488035
TO
1033 $values = array();
1034
e7e657f0 1035 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
9e74e3ce 1036 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
e96f025a 1037 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
034500d4 1038 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
9e74e3ce 1039
6a488035
TO
1040 // CRM-5081 - formatting the dates to omit seconds.
1041 // Note the 00 in the date format string is needed otherwise later on it thinks scheduled ones are overdue.
ad280fb6
JL
1042 $select = "
1043 SELECT COUNT(ca.id) AS ismultiple,
1044 ca.id AS id,
1045 ca.activity_type_id AS type,
1046 ca.activity_type_id AS activity_type_id,
1047 tcc.sort_name AS target_contact_name,
1048 tcc.id AS target_contact_id,
1049 scc.sort_name AS source_contact_name,
1050 scc.id AS source_contact_id,
1051 acc.sort_name AS assignee_contact_name,
1052 acc.id AS assignee_contact_id,
1053 DATE_FORMAT(
1054 IF(ca.activity_date_time < NOW() AND ca.status_id=ov.value,
1055 ca.activity_date_time,
1056 DATE_ADD(NOW(), INTERVAL 1 YEAR)
1057 ), '%Y%m%d%H%i00') AS overdue_date,
1058 DATE_FORMAT(ca.activity_date_time, '%Y%m%d%H%i00') AS display_date,
1059 ca.status_id AS status,
1060 ca.subject AS subject,
1061 ca.is_deleted AS deleted,
1062 ca.priority_id AS priority,
1063 ca.weight AS weight,
1064 GROUP_CONCAT(ef.file_id) AS attachment_ids ";
6a488035 1065
e96f025a 1066 $from = "
ad280fb6
JL
1067 FROM civicrm_case_activity cca
1068 INNER JOIN civicrm_activity ca
1069 ON ca.id = cca.activity_id
1070 INNER JOIN civicrm_activity_contact cas
1071 ON cas.activity_id = ca.id
1072 AND cas.record_type_id = {$sourceID}
1073 INNER JOIN civicrm_contact scc
1074 ON scc.id = cas.contact_id
1075 LEFT JOIN civicrm_activity_contact caa
1076 ON caa.activity_id = ca.id
1077 AND caa.record_type_id = {$assigneeID}
1078 LEFT JOIN civicrm_contact acc
1079 ON acc.id = caa.contact_id
1080 LEFT JOIN civicrm_activity_contact cat
1081 ON cat.activity_id = ca.id
1082 AND cat.record_type_id = {$targetID}
1083 LEFT JOIN civicrm_contact tcc
1084 ON tcc.id = cat.contact_id
1085 INNER JOIN civicrm_option_group cog
1086 ON cog.name = 'activity_type'
1087 INNER JOIN civicrm_option_value cov
1088 ON cov.option_group_id = cog.id
1089 AND cov.value = ca.activity_type_id
1090 AND cov.is_active = 1
1091 LEFT JOIN civicrm_entity_file ef
1092 ON ef.entity_table = 'civicrm_activity'
1093 AND ef.entity_id = ca.id
1094 LEFT OUTER JOIN civicrm_option_group og
1095 ON og.name = 'activity_status'
1096 LEFT OUTER JOIN civicrm_option_value ov
1097 ON ov.option_group_id=og.id
1098 AND ov.name = 'Scheduled'";
1099
1100 $where = '
1101 WHERE cca.case_id= %1
1102 AND ca.is_current_revision = 1';
1103
1104 if (!empty($params['source_contact_id'])) {
1105 $where .= "
1106 AND cas.contact_id = " . CRM_Utils_Type::escape($params['source_contact_id'], 'Integer');
6a488035
TO
1107 }
1108
a7488080 1109 if (!empty($params['status_id'])) {
ad280fb6
JL
1110 $where .= "
1111 AND ca.status_id = " . CRM_Utils_Type::escape($params['status_id'], 'Integer');
6a488035
TO
1112 }
1113
a7488080 1114 if (!empty($params['activity_deleted'])) {
ad280fb6
JL
1115 $where .= "
1116 AND ca.is_deleted = 1";
6a488035
TO
1117 }
1118 else {
ad280fb6
JL
1119 $where .= "
1120 AND ca.is_deleted = 0";
6a488035
TO
1121 }
1122
a7488080 1123 if (!empty($params['activity_type_id'])) {
ad280fb6
JL
1124 $where .= "
1125 AND ca.activity_type_id = " . CRM_Utils_Type::escape($params['activity_type_id'], 'Integer');
6a488035
TO
1126 }
1127
a7488080 1128 if (!empty($params['activity_date_low'])) {
6a488035
TO
1129 $fromActivityDate = CRM_Utils_Type::escape(CRM_Utils_Date::processDate($params['activity_date_low']), 'Date');
1130 }
ad280fb6
JL
1131 if (!empty($fromActivityDate)) {
1132 $where .= "
1133 AND ca.activity_date_time >= '{$fromActivityDate}'";
1134 }
1135
a7488080 1136 if (!empty($params['activity_date_high'])) {
6a488035
TO
1137 $toActivityDate = CRM_Utils_Type::escape(CRM_Utils_Date::processDate($params['activity_date_high']), 'Date');
1138 $toActivityDate = $toActivityDate ? $toActivityDate + 235959 : NULL;
1139 }
6a488035 1140 if (!empty($toActivityDate)) {
ad280fb6
JL
1141 $where .= "
1142 AND ca.activity_date_time <= '{$toActivityDate}'";
6a488035
TO
1143 }
1144
ad280fb6
JL
1145 $groupBy = "
1146 GROUP BY ca.id ";
6a488035 1147
ad280fb6
JL
1148 $sortBy = CRM_Utils_Array::value('sortBy', $params);
1149 if (!$sortBy) {
6a488035 1150 // CRM-5081 - added id to act like creation date
ad280fb6
JL
1151 $orderBy = "
1152 ORDER BY overdue_date ASC, display_date DESC, weight DESC";
6a488035
TO
1153 }
1154 else {
ad280fb6
JL
1155 $sortBy = CRM_Utils_Type::escape($sortBy, 'String');
1156 $orderBy = " ORDER BY $sortBy ";
6a488035
TO
1157 }
1158
3fff685f
JL
1159 $page = CRM_Utils_Array::value('page', $params);
1160 $rp = CRM_Utils_Array::value('rp', $params);
1161
6a488035 1162 if (!$page) {
6a488035 1163 $page = 1;
6a488035
TO
1164 }
1165 if (!$rp) {
1166 $rp = 10;
1167 }
6a488035 1168 $start = (($page - 1) * $rp);
ad280fb6 1169 $limit = " LIMIT $start, $rp";
6a488035 1170
ad280fb6 1171 $query = $select . $from . $where . $groupBy . $orderBy . $limit;
3fff685f 1172 $queryParams = array(1 => array($caseID, 'Integer'));
6a488035 1173
3fff685f 1174 $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
ad280fb6 1175
e96f025a 1176 $activityTypes = CRM_Case_PseudoConstant::caseActivityType(FALSE, TRUE);
ad280fb6 1177 $activityStatuses = CRM_Core_PseudoConstant::activityStatus();
6a488035
TO
1178
1179 $url = CRM_Utils_System::url("civicrm/case/activity",
1180 "reset=1&cid={$contactID}&caseid={$caseID}", FALSE, NULL, FALSE
1181 );
1182
1183 $contextUrl = '';
1184 if ($context == 'fulltext') {
1185 $contextUrl = "&context={$context}";
1186 }
e96f025a 1187 $editUrl = "{$url}&action=update{$contextUrl}";
1188 $deleteUrl = "{$url}&action=delete{$contextUrl}";
1189 $restoreUrl = "{$url}&action=renew{$contextUrl}";
15da63a8 1190 $viewTitle = ts('View activity');
5a3e23e6 1191 $statusTitle = ts('Edit Status');
6a488035
TO
1192
1193 $emailActivityTypeIDs = array(
1194 'Email' => CRM_Core_OptionGroup::getValue('activity_type',
1195 'Email',
1196 'name'
1197 ),
1198 'Inbound Email' => CRM_Core_OptionGroup::getValue('activity_type',
1199 'Inbound Email',
1200 'name'
1201 ),
1202 );
1203
1204 $emailActivityTypeIDs = array(
1205 'Email' => CRM_Core_OptionGroup::getValue('activity_type',
1206 'Email',
1207 'name'
1208 ),
1209 'Inbound Email' => CRM_Core_OptionGroup::getValue('activity_type',
1210 'Inbound Email',
1211 'name'
1212 ),
1213 );
1214
1215 $caseDeleted = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseID, 'is_deleted');
1216
1217 // define statuses which are handled like Completed status (others are assumed to be handled like Scheduled status)
1218 $compStatusValues = array();
1219 $compStatusNames = array('Completed', 'Left Message', 'Cancelled', 'Unreachable', 'Not Required');
1220 foreach ($compStatusNames as $name) {
1221 $compStatusValues[] = CRM_Core_OptionGroup::getValue('activity_status', $name, 'name');
1222 }
ad280fb6
JL
1223
1224 $contactViewUrl = CRM_Utils_System::url("civicrm/contact/view", "reset=1&cid=", FALSE, NULL, FALSE);
6a488035
TO
1225 $hasViewContact = CRM_Core_Permission::giveMeAllACLs();
1226 $clientIds = self::retrieveContactIdsByCaseId($caseID);
1227
1228 if (!$userID) {
1229 $session = CRM_Core_Session::singleton();
1230 $userID = $session->get('userID');
1231 }
1232
ad280fb6 1233 $caseActivities = array();
3fff685f 1234 $caseCount = 0;
ad280fb6 1235
6a488035 1236 while ($dao->fetch()) {
3fff685f 1237 $caseCount++;
ad280fb6
JL
1238 $caseActivity = array();
1239 $caseActivityId = $dao->id;
6a488035 1240
ad280fb6
JL
1241 $allowView = self::checkPermission($caseActivityId, 'view', $dao->activity_type_id, $userID);
1242 $allowEdit = self::checkPermission($caseActivityId, 'edit', $dao->activity_type_id, $userID);
1243 $allowDelete = self::checkPermission($caseActivityId, 'delete', $dao->activity_type_id, $userID);
6a488035
TO
1244
1245 //do not have sufficient permission
1246 //to access given case activity record.
1247 if (!$allowView && !$allowEdit && !$allowDelete) {
1248 continue;
1249 }
1250
ad280fb6
JL
1251 //Add classes to the row, via DataTables syntax
1252 $caseActivity['DT_RowClass'] = "crm-entity";
6a488035 1253
ad280fb6
JL
1254 if (CRM_Utils_Array::crmInArray($dao->status, $compStatusValues)) {
1255 $caseActivity['DT_RowClass'] .= " status-completed";
6a488035 1256 }
ad280fb6
JL
1257 else {
1258 if (CRM_Utils_Date::overdue($dao->display_date)) {
1259 $caseActivity['DT_RowClass'] .= " status-overdue";
1260 }
1261 else {
1262 $caseActivity['DT_RowClass'] .= " status-scheduled";
6a488035
TO
1263 }
1264 }
ad280fb6
JL
1265
1266 if (!empty($dao->priority)) {
1267 if ($dao->priority == CRM_Core_OptionGroup::getValue('priority', 'Urgent', 'name')) {
1268 $caseActivity['DT_RowClass'] .= " priority-urgent ";
1269 }
1270 elseif ($dao->priority == CRM_Core_OptionGroup::getValue('priority', 'Low', 'name')) {
1271 $caseActivity['DT_RowClass'] .= " priority-low ";
6a488035 1272 }
6a488035 1273 }
6a488035 1274
ad280fb6
JL
1275 //Add data to the row for inline editing, via DataTable syntax
1276 $caseActivity['DT_RowData'] = array();
1277 $caseActivity['DT_RowData']['entity'] = 'activity';
1278 $caseActivity['DT_RowData']['id'] = $caseActivityId;
6a488035 1279
ad280fb6
JL
1280 //Activity Date and Time
1281 $caseActivity['activity_date_time'] = CRM_Utils_Date::customFormat($dao->display_date);
6a488035 1282
ad280fb6
JL
1283 //Activity Subject
1284 $caseActivity['subject'] = $dao->subject;
1285
1286 //Activity Type
1287 $caseActivity['type'] = $activityTypes[$dao->type]['label'];
1288
1289 //Activity Target (With)
1290 $targetContact = '';
1291 if (isset($dao->target_contact_id)) {
1292 $targetContact = $dao->target_contact_name;
1293 if ($hasViewContact) {
1294 $targetContact = '<a href="' . $contactViewUrl . $dao->target_contact_id . '">' . $dao->target_contact_name . '</a>';
6a488035 1295 }
ad280fb6
JL
1296 }
1297 $caseActivity['target_contact_name'] = $targetContact;
1298
1299 //Activity Source Contact (Reporter)
1300 $sourceContact = $dao->source_contact_name;
1301 if ($hasViewContact) {
1302 $sourceContact = '<a href="' . $contactViewUrl . $dao->source_contact_id . '">' . $dao->source_contact_name . '</a>';
1303 }
1304 $caseActivity['source_contact_name'] = $sourceContact;
1305
1306 //Activity Assignee. CRM-4485.
1307 $assigneeContact = '';
1308 if (isset($dao->assignee_contact_id)) {
1309 $assigneeContact = $dao->assignee_contact_name;
1310 if ($hasViewContact) {
1311 $assigneeContact = '<a href="' . $contactViewUrl . $dao->assignee_contact_id . '">' . $dao->assignee_contact_name . '</a>';
6a488035
TO
1312 }
1313 }
ad280fb6
JL
1314 $caseActivity['assignee_contact_name'] = $assigneeContact;
1315
1316 //Activity Status
1317 $caseActivity['status_id'] = $activityStatuses[$dao->status];
1318
56cbe6ae 1319 // 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 1320 $url = "";
56cbe6ae 1321 $css = 'class="action-item crm-hover-button"';
ad280fb6 1322 if ($allowView) {
3fff685f
JL
1323 $viewUrl = CRM_Utils_System::url('civicrm/case/activity/view', array('cid' => $contactID, 'aid' => $caseActivityId));
1324 $url = '<a ' . str_replace('action-item', 'action-item medium-pop-up', $css) . 'href="' . $viewUrl . '" title="' . $viewTitle . '">' . ts('View') . '</a>';
ad280fb6
JL
1325 }
1326 $additionalUrl = "&id={$caseActivityId}";
6a488035
TO
1327 if (!$dao->deleted) {
1328 //hide edit link of activity type email.CRM-4530.
1329 if (!in_array($dao->type, $emailActivityTypeIDs)) {
1330 //hide Edit link if activity type is NOT editable (special case activities).CRM-5871
1331 if ($allowEdit) {
3fff685f 1332 $url .= '<a ' . $css . ' href="' . $editUrl . $additionalUrl . '">' . ts('Edit') . '</a> ';
6a488035
TO
1333 }
1334 }
1335 if ($allowDelete) {
6ce08914 1336 $url .= ' <a ' . str_replace('action-item', 'action-item small-popup', $css) . ' href="' . $deleteUrl . $additionalUrl . '">' . ts('Delete') . '</a>';
6a488035
TO
1337 }
1338 }
1339 elseif (!$caseDeleted) {
56cbe6ae 1340 $url = ' <a ' . $css . ' href="' . $restoreUrl . $additionalUrl . '">' . ts('Restore') . '</a>';
ad280fb6 1341 $caseActivity['status_id'] = $caseActivity['status_id'] . '<br /> (deleted)';
6a488035
TO
1342 }
1343
1344 //check for operations.
ad280fb6
JL
1345 if (self::checkPermission($caseActivityId, 'Move To Case', $dao->activity_type_id)) {
1346 $url .= ' <a ' . $css . ' href="#" onClick="Javascript:fileOnCase( \'move\',' . $caseActivityId . ', ' . $caseID . ', this ); return false;">' . ts('Move To Case') . '</a> ';
6a488035 1347 }
ad280fb6
JL
1348 if (self::checkPermission($caseActivityId, 'Copy To Case', $dao->activity_type_id)) {
1349 $url .= ' <a ' . $css . ' href="#" onClick="Javascript:fileOnCase( \'copy\',' . $caseActivityId . ',' . $caseID . ', this ); return false;">' . ts('Copy To Case') . '</a> ';
6a488035
TO
1350 }
1351 // if there are file attachments we will return how many and, if only one, add a link to it
e96f025a 1352 if (!empty($dao->attachment_ids)) {
1353 $attachmentIDs = explode(',', $dao->attachment_ids);
ad280fb6
JL
1354 $caseActivity['no_attachments'] = count($attachmentIDs);
1355 if ($caseActivity['no_attachments'] == 1) {
6a488035
TO
1356 // if there is only one it's easy to do a link - otherwise just flag it
1357 $attachmentViewUrl = CRM_Utils_System::url(
1358 "civicrm/file",
ad280fb6 1359 "reset=1&eid=" . $caseActivityId . "&id=" . $dao->attachment_ids,
6a488035
TO
1360 FALSE,
1361 NULL,
1362 FALSE
1363 );
56cbe6ae 1364 $url .= " <a href='$attachmentViewUrl' ><span class='icon paper-icon'></span></a>";
6a488035
TO
1365 }
1366 }
1367
ad280fb6 1368 $caseActivity['links'] = $url;
6a488035 1369
ad280fb6 1370 array_push($caseActivities, $caseActivity);
6a488035
TO
1371 }
1372 $dao->free();
1373
ad280fb6
JL
1374 $caseActivitiesDT = array();
1375 $caseActivitiesDT['data'] = $caseActivities;
3fff685f
JL
1376 $caseActivitiesDT['recordsTotal'] = $caseCount;
1377 $caseActivitiesDT['recordsFiltered'] = $caseCount;
ad280fb6
JL
1378
1379 return $caseActivitiesDT;
6a488035
TO
1380 }
1381
1382 /**
d2e5d2ce 1383 * Get Case Related Contacts.
6a488035 1384 *
64bd5a0e
TO
1385 * @param int $caseID
1386 * Case id.
1387 * @param bool $skipDetails
1388 * If true include details of contacts.
6a488035 1389 *
a6c01b45
CW
1390 * @return array
1391 * array of return properties
6a488035 1392 *
6a488035 1393 */
00be9182 1394 public static function getRelatedContacts($caseID, $skipDetails = FALSE) {
6a488035 1395 $values = array();
68783143
BS
1396 $query = '
1397 SELECT cc.display_name as name, cc.sort_name as sort_name, cc.id, crt.label_b_a as role, ce.email
1398 FROM civicrm_relationship cr
1399 LEFT JOIN civicrm_relationship_type crt
1400 ON crt.id = cr.relationship_type_id
1401 LEFT JOIN civicrm_contact cc
1402 ON cc.id = cr.contact_id_b
1403 LEFT JOIN civicrm_email ce
1404 ON ce.contact_id = cc.id
1405 AND ce.is_primary= 1
1406 WHERE cr.case_id = %1
1407 GROUP BY cc.id';
6a488035
TO
1408
1409 $params = array(1 => array($caseID, 'Integer'));
1410 $dao = CRM_Core_DAO::executeQuery($query, $params);
1411
1412 while ($dao->fetch()) {
1413 if ($skipDetails) {
1414 $values[$dao->id] = 1;
1415 }
1416 else {
1417 $values[] = array(
1418 'contact_id' => $dao->id,
1419 'display_name' => $dao->name,
1420 'sort_name' => $dao->sort_name,
1421 'role' => $dao->role,
1422 'email' => $dao->email,
1423 );
1424 }
1425 }
1426 $dao->free();
1427
1428 return $values;
1429 }
1430
1431 /**
100fef9d 1432 * Send e-mail copy of activity
6a488035 1433 *
100fef9d 1434 * @param int $clientId
64bd5a0e
TO
1435 * @param int $activityId
1436 * Activity Id.
1437 * @param array $contacts
1438 * Array of related contact.
6a488035 1439 *
77b97be7 1440 * @param null $attachments
100fef9d 1441 * @param int $caseId
77b97be7 1442 *
67d19299 1443 * @return bool |array
6a488035 1444 */
00be9182 1445 public static function sendActivityCopy($clientId, $activityId, $contacts, $attachments = NULL, $caseId) {
6a488035 1446 if (!$activityId) {
67d19299 1447 return FALSE;
6a488035
TO
1448 }
1449
1450 $tplParams = $activityInfo = array();
1451 //if its a case activity
1452 if ($caseId) {
1453 $activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id');
1454 $nonCaseActivityTypes = CRM_Core_PseudoConstant::activityType();
a7488080 1455 if (!empty($nonCaseActivityTypes[$activityTypeId])) {
6a488035
TO
1456 $anyActivity = TRUE;
1457 }
1458 else {
1459 $anyActivity = FALSE;
1460 }
1461 $tplParams['isCaseActivity'] = 1;
1462 $tplParams['client_id'] = $clientId;
1463 }
1464 else {
1465 $anyActivity = TRUE;
1466 }
1467
1468 $xmlProcessorProcess = new CRM_Case_XMLProcessor_Process();
1469 $isRedact = $xmlProcessorProcess->getRedactActivityEmail();
1470
1471 $xmlProcessorReport = new CRM_Case_XMLProcessor_Report();
1472
1473 $activityInfo = $xmlProcessorReport->getActivityInfo($clientId, $activityId, $anyActivity, $isRedact);
1474 if ($caseId) {
1475 $activityInfo['fields'][] = array('label' => 'Case ID', 'type' => 'String', 'value' => $caseId);
1476 }
1477 $tplParams['activity'] = $activityInfo;
1478 foreach ($tplParams['activity']['fields'] as $k => $val) {
1479 if (CRM_Utils_Array::value('label', $val) == ts('Subject')) {
1480 $activitySubject = $val['value'];
1481 break;
1482 }
1483 }
1484 $session = CRM_Core_Session::singleton();
1485 // CRM-8926 If user is not logged in, use the activity creator as userID
1486 if (!($userID = $session->get('userID'))) {
4322672b 1487 $userID = CRM_Activity_BAO_Activity::getSourceContactID($activityId);
6a488035
TO
1488 }
1489
1490 //also create activities simultaneously of this copy.
1491 $activityParams = array();
1492
1493 $activityParams['source_record_id'] = $activityId;
1494 $activityParams['source_contact_id'] = $userID;
1495 $activityParams['activity_type_id'] = CRM_Core_OptionGroup::getValue('activity_type', 'Email', 'name');
1496 $activityParams['activity_date_time'] = date('YmdHis');
1497 $activityParams['status_id'] = CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name');
1498 $activityParams['medium_id'] = CRM_Core_OptionGroup::getValue('encounter_medium', 'email', 'name');
1499 $activityParams['case_id'] = $caseId;
1500 $activityParams['is_auto'] = 0;
1501 $activityParams['target_id'] = $clientId;
1502
1503 $tplParams['activitySubject'] = $activitySubject;
1504
1505 // if it’s a case activity, add hashed id to the template (CRM-5916)
1506 if ($caseId) {
1507 $tplParams['idHash'] = substr(sha1(CIVICRM_SITE_KEY . $caseId), 0, 7);
1508 }
1509
1510 $result = array();
1511 list($name, $address) = CRM_Contact_BAO_Contact_Location::getEmailDetails($userID);
1512
1513 $receiptFrom = "$name <$address>";
1514
1515 $recordedActivityParams = array();
1516
1517 foreach ($contacts as $mail => $info) {
1518 $tplParams['contact'] = $info;
1519 self::buildPermissionLinks($tplParams, $activityParams);
1520
21827a41 1521 $displayName = CRM_Utils_Array::value('display_name', $info);
6a488035 1522
c6327d7d 1523 list($result[CRM_Utils_Array::value('contact_id', $info)], $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate(
6a488035
TO
1524 array(
1525 'groupName' => 'msg_tpl_workflow_case',
1526 'valueName' => 'case_activity',
21827a41 1527 'contactId' => CRM_Utils_Array::value('contact_id', $info),
6a488035
TO
1528 'tplParams' => $tplParams,
1529 'from' => $receiptFrom,
1530 'toName' => $displayName,
1531 'toEmail' => $mail,
1532 'attachments' => $attachments,
1533 )
1534 );
1535
1536 $activityParams['subject'] = $activitySubject . ' - copy sent to ' . $displayName;
1537 $activityParams['details'] = $message;
1538
21827a41 1539 if (!empty($result[$info['contact_id']])) {
6a488035
TO
1540 /*
1541 * Really only need to record one activity with all the targets combined.
1542 * Originally the template was going to possibly have different content, e.g. depending on permissions,
1543 * but it's always the same content at the moment.
1544 */
1545 if (empty($recordedActivityParams)) {
1546 $recordedActivityParams = $activityParams;
1547 }
1548 else {
1549 $recordedActivityParams['subject'] .= "; $displayName";
1550 }
1551 $recordedActivityParams['target_contact_id'][] = $info['contact_id'];
1552 }
1553 else {
21827a41 1554 unset($result[CRM_Utils_Array::value('contact_id', $info)]);
6a488035
TO
1555 }
1556 }
1557
1558 if (!empty($recordedActivityParams)) {
1559 $activity = CRM_Activity_BAO_Activity::create($recordedActivityParams);
1560
1561 //create case_activity record if its case activity.
1562 if ($caseId) {
1563 $caseParams = array(
1564 'activity_id' => $activity->id,
1565 'case_id' => $caseId,
1566 );
1567 self::processCaseActivity($caseParams);
1568 }
1569 }
1570
1571 return $result;
1572 }
1573
1574 /**
1575 * Retrieve count of activities having a particular type, and
1576 * associated with a particular case.
1577 *
64bd5a0e
TO
1578 * @param int $caseId
1579 * ID of the case.
1580 * @param int $activityTypeId
1581 * ID of the activity type.
6a488035
TO
1582 *
1583 * @return array
6a488035 1584 */
00be9182 1585 public static function getCaseActivityCount($caseId, $activityTypeId) {
e96f025a 1586 $queryParam = array(
1587 1 => array($caseId, 'Integer'),
6a488035
TO
1588 2 => array($activityTypeId, 'Integer'),
1589 );
1590 $query = "SELECT count(ca.id) as countact
1591 FROM civicrm_activity ca
1592 INNER JOIN civicrm_case_activity cca ON ca.id = cca.activity_id
1593 WHERE ca.activity_type_id = %2
1594 AND cca.case_id = %1
1595 AND ca.is_deleted = 0";
1596
1597 $dao = CRM_Core_DAO::executeQuery($query, $queryParam);
1598 if ($dao->fetch()) {
1599 return $dao->countact;
1600 }
1601
1602 return FALSE;
1603 }
1604
1605 /**
d2e5d2ce 1606 * Create an activity for a case via email.
6a488035 1607 *
64bd5a0e
TO
1608 * @param int $file
1609 * Email sent.
6a488035 1610 *
72b3a70c
CW
1611 * @return array|void
1612 * $activity object of newly creted activity via email
6a488035 1613 */
00be9182 1614 public static function recordActivityViaEmail($file) {
6a488035
TO
1615 if (!file_exists($file) ||
1616 !is_readable($file)
1617 ) {
1618 return CRM_Core_Error::fatal(ts('File %1 does not exist or is not readable',
d6f468d3
KJ
1619 array(1 => $file)
1620 ));
6a488035
TO
1621 }
1622
1623 $result = CRM_Utils_Mail_Incoming::parse($file);
1624 if ($result['is_error']) {
1625 return $result;
1626 }
1627
1628 foreach ($result['to'] as $to) {
1629 $caseId = NULL;
1630
1631 $emailPattern = '/^([A-Z0-9._%+-]+)\+([\d]+)@[A-Z0-9.-]+\.[A-Z]{2,4}$/i';
1632 $replacement = preg_replace($emailPattern, '$2', $to['email']);
1633
1634 if ($replacement !== $to['email']) {
1635 $caseId = $replacement;
1636 //if caseId is invalid, return as error file
1637 if (!CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $caseId, 'id')) {
1638 return CRM_Core_Error::createAPIError(ts('Invalid case ID ( %1 ) in TO: field.',
d6f468d3
KJ
1639 array(1 => $caseId)
1640 ));
6a488035
TO
1641 }
1642 }
1643 else {
1644 continue;
1645 }
1646
1647 // TODO: May want to replace this with a call to getRelatedAndGlobalContacts() when this feature is revisited.
1648 // (Or for efficiency call the global one outside the loop and then union with this each time.)
1649 $contactDetails = self::getRelatedContacts($caseId, TRUE);
1650
a7488080 1651 if (!empty($contactDetails[$result['from']['id']])) {
6a488035
TO
1652 $params = array();
1653 $params['subject'] = $result['subject'];
1654 $params['activity_date_time'] = $result['date'];
1655 $params['details'] = $result['body'];
1656 $params['source_contact_id'] = $result['from']['id'];
1657 $params['status_id'] = CRM_Core_OptionGroup::getValue('activity_status',
1658 'Completed',
1659 'name'
1660 );
1661
1662 $details = CRM_Case_PseudoConstant::caseActivityType();
1663 $matches = array();
1664 preg_match('/^\W+([a-zA-Z0-9_ ]+)(\W+)?\n/i',
1665 $result['body'], $matches
1666 );
1667
1668 if (!empty($matches) && isset($matches[1])) {
1669 $activityType = trim($matches[1]);
1670 if (isset($details[$activityType])) {
1671 $params['activity_type_id'] = $details[$activityType]['id'];
1672 }
1673 }
1674 if (!isset($params['activity_type_id'])) {
1675 $params['activity_type_id'] = CRM_Core_OptionGroup::getValue('activity_type', 'Inbound Email', 'name');
1676 }
1677
1678 // create activity
1679 $activity = CRM_Activity_BAO_Activity::create($params);
1680
1681 $caseParams = array(
1682 'activity_id' => $activity->id,
1683 'case_id' => $caseId,
1684 );
1685 self::processCaseActivity($caseParams);
1686 }
1687 else {
1688 return CRM_Core_Error::createAPIError(ts('FROM email contact %1 doesn\'t have a relationship to the referenced case.',
d6f468d3
KJ
1689 array(1 => $result['from']['email'])
1690 ));
6a488035
TO
1691 }
1692 }
1693 }
1694
1695 /**
d2e5d2ce 1696 * Retrieve the scheduled activity type and date.
6a488035 1697 *
64bd5a0e
TO
1698 * @param array $cases
1699 * Array of contact and case id.
77b97be7
EM
1700 *
1701 * @param string $type
6a488035 1702 *
a6c01b45
CW
1703 * @return array
1704 * Array of scheduled activity type and date
6a488035 1705 *
6a488035 1706 *
6a488035 1707 */
00be9182 1708 public static function getNextScheduledActivity($cases, $type = 'upcoming') {
6a488035
TO
1709 $session = CRM_Core_Session::singleton();
1710 $userID = $session->get('userID');
1711
1712 $caseID = implode(',', $cases['case_id']);
1713 $contactID = implode(',', $cases['contact_id']);
1714
1715 $condition = "
1716 AND civicrm_case_contact.contact_id IN( {$contactID} )
1717 AND civicrm_case.id IN( {$caseID})
1718 AND civicrm_case.is_deleted = {$cases['case_deleted']}";
1719
1720 $query = self::getCaseActivityQuery($type, $userID, $condition, $cases['case_deleted']);
1721
1722 $res = CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
1723
1724 $activityInfo = array();
1725 while ($res->fetch()) {
1726 if ($type == 'upcoming') {
1727 $activityInfo[$res->case_id]['date'] = $res->case_scheduled_activity_date;
1728 $activityInfo[$res->case_id]['type'] = $res->case_scheduled_activity_type;
1729 }
1730 else {
1731 $activityInfo[$res->case_id]['date'] = $res->case_recent_activity_date;
1732 $activityInfo[$res->case_id]['type'] = $res->case_recent_activity_type;
1733 }
1734 }
1735
1736 return $activityInfo;
1737 }
1738
1739 /**
d2e5d2ce 1740 * Combine all the exportable fields from the lower levels object.
6a488035 1741 *
a6c01b45
CW
1742 * @return array
1743 * array of exportable Fields
6a488035 1744 */
00be9182 1745 public static function &exportableFields() {
6a488035
TO
1746 if (!self::$_exportableFields) {
1747 if (!self::$_exportableFields) {
1748 self::$_exportableFields = array();
1749 }
1750
e96f025a 1751 $fields = CRM_Case_DAO_Case::export();
6a488035 1752 $fields['case_role'] = array('title' => ts('Role in Case'));
e96f025a 1753 $fields['case_type'] = array(
1754 'title' => ts('Case Type'),
6a488035
TO
1755 'name' => 'case_type',
1756 );
e96f025a 1757 $fields['case_status'] = array(
1758 'title' => ts('Case Status'),
6a488035
TO
1759 'name' => 'case_status',
1760 );
1761
1762 self::$_exportableFields = $fields;
1763 }
1764 return self::$_exportableFields;
1765 }
1766
1767 /**
d2e5d2ce 1768 * Restore the record that are associated with this case.
6a488035 1769 *
64bd5a0e
TO
1770 * @param int $caseId
1771 * Id of the case to restore.
6a488035 1772 *
72b3a70c 1773 * @return bool
6a488035 1774 */
00be9182 1775 public static function restoreCase($caseId) {
6a488035
TO
1776 //restore activities
1777 $activities = self::getCaseActivityDates($caseId);
1778 if ($activities) {
1779 foreach ($activities as $value) {
1780 CRM_Activity_BAO_Activity::restoreActivity($value);
1781 }
1782 }
1783 //restore case
e96f025a 1784 $case = new CRM_Case_DAO_Case();
1785 $case->id = $caseId;
6a488035
TO
1786 $case->is_deleted = 0;
1787 $case->save();
1788
1789 //CRM-7364, enable relationships
1790 self::enableDisableCaseRelationships($caseId, TRUE);
1791 return TRUE;
1792 }
1793
4c6ce474
EM
1794 /**
1795 * @param $groupInfo
1796 * @param null $sort
1797 * @param null $showLinks
1798 * @param bool $returnOnlyCount
1799 * @param int $offset
1800 * @param int $rowCount
1801 *
1802 * @return array
1803 */
00be9182 1804 public static function getGlobalContacts(&$groupInfo, $sort = NULL, $showLinks = NULL, $returnOnlyCount = FALSE, $offset = 0, $rowCount = 25) {
6a488035
TO
1805 $globalContacts = array();
1806
1807 $settingsProcessor = new CRM_Case_XMLProcessor_Settings();
1808 $settings = $settingsProcessor->run();
1809 if (!empty($settings)) {
1810 $groupInfo['name'] = $settings['groupname'];
1811 if ($groupInfo['name']) {
1812 $searchParams = array('name' => $groupInfo['name']);
1813 $results = array();
1814 CRM_Contact_BAO_Group::retrieve($searchParams, $results);
1815 if ($results) {
e96f025a 1816 $groupInfo['id'] = $results['id'];
6a488035 1817 $groupInfo['title'] = $results['title'];
b3188db2 1818 $params = array(array('group', '=', $groupInfo['id'], 0, 0));
e96f025a 1819 $return = array('contact_id' => 1, 'sort_name' => 1, 'display_name' => 1, 'email' => 1, 'phone' => 1);
d79c94d5 1820 list($globalContacts) = CRM_Contact_BAO_Query::apiQuery($params, $return, NULL, $sort, $offset, $rowCount, TRUE, $returnOnlyCount);
6a488035
TO
1821
1822 if ($returnOnlyCount) {
1823 return $globalContacts;
1824 }
1825
1826 if ($showLinks) {
e96f025a 1827 foreach ($globalContacts as $idx => $contact) {
d79c94d5 1828 $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
1829 }
1830 }
1831 }
1832 }
1833 }
1834 return $globalContacts;
1835 }
1836
4c6ce474 1837 /**
d2e5d2ce 1838 * Convenience function to get both case contacts and global in one array.
100fef9d 1839 * @param int $caseId
4c6ce474
EM
1840 *
1841 * @return array
1842 */
00be9182 1843 public static function getRelatedAndGlobalContacts($caseId) {
6a488035
TO
1844 $relatedContacts = self::getRelatedContacts($caseId);
1845
1846 $groupInfo = array();
1847 $globalContacts = self::getGlobalContacts($groupInfo);
1848
1849 //unset values which are not required.
1850 foreach ($globalContacts as $k => & $v) {
1851 unset($v['email_id']);
1852 unset($v['group_contact_id']);
1853 unset($v['status']);
1854 unset($v['phone']);
1855 $v['role'] = $groupInfo['title'];
1856 }
1857 //include multiple listings for the same contact/different roles.
1858 $relatedGlobalContacts = array_merge($relatedContacts, $globalContacts);
1859 return $relatedGlobalContacts;
1860 }
1861
1862 /**
100fef9d 1863 * Get Case ActivitiesDueDates with given criteria.
6a488035 1864 *
64bd5a0e
TO
1865 * @param int $caseID
1866 * Case id.
1867 * @param array $criteriaParams
1868 * Given criteria.
1869 * @param bool $latestDate
72b3a70c 1870 * If set newest or oldest date is selected.
6a488035 1871 *
72b3a70c
CW
1872 * @return array
1873 * case activities due dates
6a488035 1874 *
6a488035 1875 */
00be9182 1876 public static function getCaseActivityDates($caseID, $criteriaParams = array(), $latestDate = FALSE) {
e96f025a 1877 $values = array();
6a488035 1878 $selectDate = " ca.activity_date_time";
e96f025a 1879 $where = $groupBy = ' ';
6a488035
TO
1880
1881 if (!$caseID) {
408b79bf 1882 return NULL;
6a488035
TO
1883 }
1884
1885 if ($latestDate) {
a7488080 1886 if (!empty($criteriaParams['activity_type_id'])) {
6a488035
TO
1887 $where .= " AND ca.activity_type_id = " . CRM_Utils_Type::escape($criteriaParams['activity_type_id'], 'Integer');
1888 $where .= " AND ca.is_current_revision = 1";
1889 $groupBy .= " GROUP BY ca.activity_type_id";
1890 }
1891
a7488080 1892 if (!empty($criteriaParams['newest'])) {
6a488035
TO
1893 $selectDate = " max(ca.activity_date_time) ";
1894 }
1895 else {
1896 $selectDate = " min(ca.activity_date_time) ";
1897 }
1898 }
1899
1900 $query = "SELECT ca.id, {$selectDate} as activity_date
1901 FROM civicrm_activity ca
1902 LEFT JOIN civicrm_case_activity cca ON cca.activity_id = ca.id LEFT JOIN civicrm_case cc ON cc.id = cca.case_id
1903 WHERE cc.id = %1 {$where} {$groupBy}";
1904
1905 $params = array(1 => array($caseID, 'Integer'));
1906 $dao = CRM_Core_DAO::executeQuery($query, $params);
1907
1908 while ($dao->fetch()) {
1909 $values[$dao->id]['id'] = $dao->id;
1910 $values[$dao->id]['activity_date'] = $dao->activity_date;
1911 }
1912 $dao->free();
1913 return $values;
1914 }
1915
1916 /**
100fef9d 1917 * Create activities when Case or Other roles assigned/modified/deleted.
6a488035 1918 *
100fef9d 1919 * @param int $caseId
64bd5a0e
TO
1920 * @param int $relationshipId
1921 * Relationship id.
1922 * @param int $relContactId
1923 * Case role assignee contactId.
100fef9d 1924 * @param int $contactId
6a488035 1925 */
00be9182 1926 public static function createCaseRoleActivity($caseId, $relationshipId, $relContactId = NULL, $contactId = NULL) {
6a488035
TO
1927 if (!$caseId || !$relationshipId || empty($relationshipId)) {
1928 return;
1929 }
1930
1931 $queryParam = array();
1932 if (is_array($relationshipId)) {
1933 $relationshipId = implode(',', $relationshipId);
1934 $relationshipClause = " civicrm_relationship.id IN ($relationshipId)";
1935 }
1936 else {
1937 $relationshipClause = " civicrm_relationship.id = %1";
1938 $queryParam[1] = array($relationshipId, 'Positive');
1939 }
1940
1941 $query = "
1942 SELECT cc.display_name as clientName,
1943 cca.display_name as assigneeContactName,
1944 civicrm_relationship.case_id as caseId,
1945 civicrm_relationship_type.label_a_b as relation_a_b,
1946 civicrm_relationship_type.label_b_a as relation_b_a,
1947 civicrm_relationship.contact_id_b as rel_contact_id,
1948 civicrm_relationship.contact_id_a as assign_contact_id
1949 FROM civicrm_relationship_type, civicrm_relationship
1950 LEFT JOIN civicrm_contact cc ON cc.id = civicrm_relationship.contact_id_b
1951 LEFT JOIN civicrm_contact cca ON cca.id = civicrm_relationship.contact_id_a
1952 WHERE civicrm_relationship.relationship_type_id = civicrm_relationship_type.id AND {$relationshipClause}";
1953
1954 $dao = CRM_Core_DAO::executeQuery($query, $queryParam);
1955
1956 while ($dao->fetch()) {
1957 //to get valid assignee contact(s).
1958 if (isset($dao->caseId) || $dao->rel_contact_id != $contactId) {
1959 $caseRelationship = $dao->relation_a_b;
1960 $assigneContactName = $dao->clientName;
1961 $assigneContactIds[$dao->rel_contact_id] = $dao->rel_contact_id;
1962 }
1963 else {
1964 $caseRelationship = $dao->relation_b_a;
1965 $assigneContactName = $dao->assigneeContactName;
1966 $assigneContactIds[$dao->assign_contact_id] = $dao->assign_contact_id;
1967 }
1968 }
1969
1970 $session = CRM_Core_Session::singleton();
1971 $activityParams = array(
1972 'source_contact_id' => $session->get('userID'),
1973 'subject' => $caseRelationship . ' : ' . $assigneContactName,
1974 'activity_date_time' => date('YmdHis'),
1975 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'),
1976 );
1977
1978 //if $relContactId is passed, role is added or modified.
1979 if (!empty($relContactId)) {
1980 $activityParams['assignee_contact_id'] = $assigneContactIds;
1981
1982 $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type',
1983 'Assign Case Role',
1984 'name'
1985 );
1986 }
1987 else {
1988 $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type',
1989 'Remove Case Role',
1990 'name'
1991 );
1992 }
1993
1994 $activityParams['activity_type_id'] = $activityTypeID;
1995
1996 $activity = CRM_Activity_BAO_Activity::create($activityParams);
1997
1998 //create case_activity record.
1999 $caseParams = array(
2000 'activity_id' => $activity->id,
2001 'case_id' => $caseId,
2002 );
2003
2004 CRM_Case_BAO_Case::processCaseActivity($caseParams);
2005 }
2006
2007 /**
100fef9d 2008 * Get case manger
6a488035
TO
2009 * contact which is assigned a case role of case manager.
2010 *
64bd5a0e
TO
2011 * @param int $caseType
2012 * Case type.
2013 * @param int $caseId
2014 * Case id.
6a488035 2015 *
a6c01b45
CW
2016 * @return array
2017 * array of contact on success otherwise empty
6a488035 2018 *
6a488035 2019 */
00be9182 2020 public static function getCaseManagerContact($caseType, $caseId) {
6a488035 2021 if (!$caseType || !$caseId) {
408b79bf 2022 return NULL;
6a488035
TO
2023 }
2024
2025 $caseManagerContact = array();
2026 $xmlProcessor = new CRM_Case_XMLProcessor_Process();
2027
2028 $managerRoleId = $xmlProcessor->getCaseManagerRoleId($caseType);
2029
2030 if (!empty($managerRoleId)) {
2031 $managerRoleQuery = "
2032SELECT civicrm_contact.id as casemanager_id,
2033 civicrm_contact.sort_name as casemanager
2034 FROM civicrm_contact
2035 LEFT JOIN civicrm_relationship ON (civicrm_relationship.contact_id_b = civicrm_contact.id AND civicrm_relationship.relationship_type_id = %1)
2036 LEFT JOIN civicrm_case ON civicrm_case.id = civicrm_relationship.case_id
2037 WHERE civicrm_case.id = %2";
2038
2039 $managerRoleParams = array(
2040 1 => array($managerRoleId, 'Integer'),
2041 2 => array($caseId, 'Integer'),
2042 );
2043
2044 $dao = CRM_Core_DAO::executeQuery($managerRoleQuery, $managerRoleParams);
2045 if ($dao->fetch()) {
2046 $caseManagerContact['casemanager_id'] = $dao->casemanager_id;
2047 $caseManagerContact['casemanager'] = $dao->casemanager;
2048 }
2049 }
2050
2051 return $caseManagerContact;
2052 }
2053
2054 /**
cde2037d 2055 * Get all cases with no end dates.
6a488035 2056 *
77b97be7
EM
2057 * @param array $params
2058 * @param array $excludeCaseIds
2059 * @param bool $excludeDeleted
2060 *
cde2037d
EM
2061 * @param bool $includeClosed
2062 *
a6c01b45 2063 * @return array
cde2037d 2064 * Case and related data keyed on case id
6a488035 2065 */
00be9182 2066 public static function getUnclosedCases($params = array(), $excludeCaseIds = array(), $excludeDeleted = TRUE, $includeClosed = FALSE) {
6a488035 2067 //params from ajax call.
3c0b6a40 2068 $where = array($includeClosed ? '(1)' : '(ca.end_date is null)');
6a488035 2069 if ($caseType = CRM_Utils_Array::value('case_type', $params)) {
8ffdec17 2070 $where[] = "( civicrm_case_type.title LIKE '%$caseType%' )";
6a488035
TO
2071 }
2072 if ($sortName = CRM_Utils_Array::value('sort_name', $params)) {
e96f025a 2073 $config = CRM_Core_Config::singleton();
2074 $search = ($config->includeWildCardInName) ? "%$sortName%" : "$sortName%";
6a488035
TO
2075 $where[] = "( sort_name LIKE '$search' )";
2076 }
bd343dcc
CW
2077 if ($cid = CRM_Utils_Array::value('contact_id', $params)) {
2078 $where[] = " c.id = $cid ";
2079 }
6a488035
TO
2080 if (is_array($excludeCaseIds) &&
2081 !CRM_Utils_System::isNull($excludeCaseIds)
2082 ) {
2083 $where[] = ' ( ca.id NOT IN ( ' . implode(',', $excludeCaseIds) . ' ) ) ';
2084 }
2085 if ($excludeDeleted) {
2086 $where[] = ' ( ca.is_deleted = 0 OR ca.is_deleted IS NULL ) ';
2087 }
2088
2089 //filter for permissioned cases.
2090 $filterCases = array();
2091 $doFilterCases = FALSE;
2092 if (!CRM_Core_Permission::check('access all cases and activities')) {
2093 $doFilterCases = TRUE;
e96f025a 2094 $session = CRM_Core_Session::singleton();
2095 $filterCases = CRM_Case_BAO_Case::getCases(FALSE, $session->get('userID'));
6a488035
TO
2096 }
2097 $whereClause = implode(' AND ', $where);
2098
2099 $limitClause = '';
2100 if ($limit = CRM_Utils_Array::value('limit', $params)) {
2101 $limitClause = "LIMIT 0, $limit";
2102 }
2103
2104 $query = "
2105 SELECT c.id as contact_id,
2106 c.sort_name,
2107 ca.id,
2108 ca.subject as case_subject,
8ffdec17 2109 civicrm_case_type.title as case_type,
3c0b6a40
CW
2110 ca.start_date as start_date,
2111 ca.end_date as end_date,
2112 ca.status_id
6a488035
TO
2113 FROM civicrm_case ca INNER JOIN civicrm_case_contact cc ON ca.id=cc.case_id
2114 INNER JOIN civicrm_contact c ON cc.contact_id=c.id
8ffdec17 2115 INNER JOIN civicrm_case_type ON ca.case_type_id = civicrm_case_type.id
6a488035 2116 WHERE {$whereClause}
3c0b6a40 2117 ORDER BY c.sort_name, ca.end_date
6a488035
TO
2118 {$limitClause}
2119";
2120 $dao = CRM_Core_DAO::executeQuery($query);
16fb2340
C
2121 $statuses = CRM_Case_BAO_Case::buildOptions('status_id', 'create');
2122
6a488035
TO
2123 $unclosedCases = array();
2124 while ($dao->fetch()) {
2125 if ($doFilterCases && !array_key_exists($dao->id, $filterCases)) {
2126 continue;
2127 }
2128 $unclosedCases[$dao->id] = array(
2129 'sort_name' => $dao->sort_name,
2130 'case_type' => $dao->case_type,
2131 'contact_id' => $dao->contact_id,
2132 'start_date' => $dao->start_date,
3c0b6a40 2133 'end_date' => $dao->end_date,
6a488035 2134 'case_subject' => $dao->case_subject,
3c0b6a40 2135 'case_status' => $statuses[$dao->status_id],
6a488035
TO
2136 );
2137 }
2138 $dao->free();
2139
2140 return $unclosedCases;
2141 }
2142
4c6ce474 2143 /**
100fef9d 2144 * @param int $contactId
4c6ce474
EM
2145 * @param bool $excludeDeleted
2146 *
2147 * @return null|string
2148 */
00be9182 2149 public static function caseCount($contactId = NULL, $excludeDeleted = TRUE) {
6a488035
TO
2150 $whereConditions = array();
2151 if ($excludeDeleted) {
2152 $whereConditions[] = "( civicrm_case.is_deleted = 0 OR civicrm_case.is_deleted IS NULL )";
2153 }
2154 if ($contactId) {
2155 $whereConditions[] = "civicrm_case_contact.contact_id = {$contactId}";
2156 }
2157 if (!CRM_Core_Permission::check('access all cases and activities')) {
2158 static $accessibleCaseIds;
2159 if (!is_array($accessibleCaseIds)) {
2160 $session = CRM_Core_Session::singleton();
ef0ae32d 2161 $accessibleCaseIds = array_keys(self::getCases(FALSE, $session->get('userID'), 'any'));
6a488035
TO
2162 }
2163 //no need of further processing.
2164 if (empty($accessibleCaseIds)) {
2165 return 0;
2166 }
2167 $whereConditions[] = "( civicrm_case.id in (" . implode(',', $accessibleCaseIds) . ") )";
2168 }
2169
2170 $whereClause = '';
2171 if (!empty($whereConditions)) {
2172 $whereClause = "WHERE " . implode(' AND ', $whereConditions);
2173 }
2174
2175 $query = "
2176 SELECT count( civicrm_case.id )
2177 FROM civicrm_case
2178LEFT JOIN civicrm_case_contact ON ( civicrm_case.id = civicrm_case_contact.case_id )
2179 {$whereClause}";
2180
2181 return CRM_Core_DAO::singleValueQuery($query);
2182 }
2183
2184 /**
2185 * Retrieve cases related to particular contact.
2186 *
64bd5a0e
TO
2187 * @param int $contactId
2188 * Contact id.
2189 * @param bool $excludeDeleted
2190 * Do not include deleted cases.
6a488035 2191 *
72b3a70c 2192 * @return array
6a488035 2193 */
00be9182 2194 public static function getContactCases($contactId, $excludeDeleted = TRUE) {
6a488035
TO
2195 $cases = array();
2196 if (!$contactId) {
2197 return $cases;
2198 }
2199
2200 $whereClause = "civicrm_case_contact.contact_id = %1";
2201 if ($excludeDeleted) {
2202 $whereClause .= " AND ( civicrm_case.is_deleted = 0 OR civicrm_case.is_deleted IS NULL )";
2203 }
2204
2205 $query = "
8ffdec17 2206 SELECT civicrm_case.id, civicrm_case_type.title as case_type, civicrm_case.start_date
6a488035
TO
2207 FROM civicrm_case
2208INNER JOIN civicrm_case_contact ON ( civicrm_case.id = civicrm_case_contact.case_id )
8ffdec17 2209 LEFT JOIN civicrm_case_type ON civicrm_case.case_type_id = civicrm_case_type.id
6a488035
TO
2210 WHERE {$whereClause}";
2211
2212 $dao = CRM_Core_DAO::executeQuery($query, array(1 => array($contactId, 'Integer')));
2213 while ($dao->fetch()) {
2214 $cases[$dao->id] = array(
2215 'case_id' => $dao->id,
2216 'case_type' => $dao->case_type,
2217 'case_start_date' => $dao->start_date,
2218 );
2219 }
2220 $dao->free();
2221
2222 return $cases;
2223 }
2224
2225 /**
2226 * Retrieve related cases for give case.
2227 *
64bd5a0e
TO
2228 * @param int $mainCaseId
2229 * Id of main case.
2230 * @param int $contactId
2231 * Id of contact.
2232 * @param bool $excludeDeleted
2233 * Do not include deleted cases.
6a488035 2234 *
72b3a70c 2235 * @return array
6a488035 2236 */
00be9182 2237 public static function getRelatedCases($mainCaseId, $contactId, $excludeDeleted = TRUE) {
6a488035
TO
2238 //FIXME : do check for permissions.
2239
2240 $relatedCases = array();
2241 if (!$mainCaseId || !$contactId) {
2242 return $relatedCases;
2243 }
2244
2245 $linkActType = array_search('Link Cases',
2246 CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name')
2247 );
2248 if (!$linkActType) {
2249 return $relatedCases;
2250 }
2251
2252 $whereClause = "mainCase.id = %2";
2253 if ($excludeDeleted) {
2254 $whereClause .= " AND ( relAct.is_deleted = 0 OR relAct.is_deleted IS NULL )";
2255 }
2256
2257 //1. first fetch related case ids.
2258 $query = "
2259 SELECT relCaseAct.case_id
2260 FROM civicrm_case mainCase
2261 INNER JOIN civicrm_case_activity mainCaseAct ON (mainCaseAct.case_id = mainCase.id)
2262 INNER JOIN civicrm_activity mainAct ON (mainCaseAct.activity_id = mainAct.id AND mainAct.activity_type_id = %1)
2263 INNER JOIN civicrm_case_activity relCaseAct ON (relCaseAct.activity_id = mainAct.id AND mainCaseAct.id != relCaseAct.id)
2264 INNER JOIN civicrm_activity relAct ON (relCaseAct.activity_id = relAct.id AND relAct.activity_type_id = %1)
2265 WHERE $whereClause";
2266
2267 $dao = CRM_Core_DAO::executeQuery($query, array(
2268 1 => array($linkActType, 'Integer'),
2269 2 => array($mainCaseId, 'Integer'),
2270 ));
2271 $relatedCaseIds = array();
2272 while ($dao->fetch()) {
2273 $relatedCaseIds[$dao->case_id] = $dao->case_id;
2274 }
2275 $dao->free();
2276
2277 // there are no related cases.
2278 if (empty($relatedCaseIds)) {
2279 return $relatedCases;
2280 }
2281
2282 $whereClause = 'relCase.id IN ( ' . implode(',', $relatedCaseIds) . ' )';
2283 if ($excludeDeleted) {
2284 $whereClause .= " AND ( relCase.is_deleted = 0 OR relCase.is_deleted IS NULL )";
2285 }
2286
2287 //filter for permissioned cases.
2288 $filterCases = array();
2289 $doFilterCases = FALSE;
2290 if (!CRM_Core_Permission::check('access all cases and activities')) {
2291 $doFilterCases = TRUE;
e96f025a 2292 $session = CRM_Core_Session::singleton();
2293 $filterCases = CRM_Case_BAO_Case::getCases(FALSE, $session->get('userID'));
6a488035
TO
2294 }
2295
2296 //2. fetch the details of related cases.
2297 $query = "
2298 SELECT relCase.id as id,
8ffdec17 2299 civicrm_case_type.title as case_type,
6a488035
TO
2300 client.display_name as client_name,
2301 client.id as client_id
2302 FROM civicrm_case relCase
2303 INNER JOIN civicrm_case_contact relCaseContact ON ( relCase.id = relCaseContact.case_id )
2304 INNER JOIN civicrm_contact client ON ( client.id = relCaseContact.contact_id )
8ffdec17 2305 LEFT JOIN civicrm_case_type ON relCase.case_type_id = civicrm_case_type.id
6a488035
TO
2306 WHERE {$whereClause}";
2307
e96f025a 2308 $dao = CRM_Core_DAO::executeQuery($query);
6a488035
TO
2309 $contactViewUrl = CRM_Utils_System::url("civicrm/contact/view", "reset=1&cid=");
2310 $hasViewContact = CRM_Core_Permission::giveMeAllACLs();
2311
2312 while ($dao->fetch()) {
2313 $caseView = NULL;
2314 if (!$doFilterCases || array_key_exists($dao->id, $filterCases)) {
2315 $caseViewStr = "reset=1&id={$dao->id}&cid={$dao->client_id}&action=view&context=case&selectedChild=case";
2316 $caseViewUrl = CRM_Utils_System::url("civicrm/contact/view/case", $caseViewStr);
6ce08914 2317 $caseView = "<a class='action-item no-popup crm-hover-button' href='{$caseViewUrl}'>" . ts('View Case') . "</a>";
6a488035
TO
2318 }
2319 $clientView = $dao->client_name;
2320 if ($hasViewContact) {
2321 $clientView = "<a href='{$contactViewUrl}{$dao->client_id}'>$dao->client_name</a>";
2322 }
2323
2324 $relatedCases[$dao->id] = array(
2325 'case_id' => $dao->id,
2326 'case_type' => $dao->case_type,
2327 'client_name' => $clientView,
2328 'links' => $caseView,
2329 );
2330 }
2331 $dao->free();
2332
2333 return $relatedCases;
2334 }
2335
2336 /**
2337 * Merge two duplicate contacts' cases - follow CRM-5758 rules.
2338 *
2339 * @see CRM_Dedupe_Merger::cpTables()
2340 *
2341 * TODO: use the 3rd $sqls param to append sql statements rather than executing them here
cde2037d
EM
2342 *
2343 * @param int $mainContactId
2344 * @param int $otherContactId
6a488035 2345 */
00be9182 2346 public static function mergeContacts($mainContactId, $otherContactId) {
6a488035
TO
2347 self::mergeCases($mainContactId, NULL, $otherContactId);
2348 }
2349
2350 /**
2351 * Function perform two task.
2352 * 1. Merge two duplicate contacts cases - follow CRM-5758 rules.
2353 * 2. Merge two cases of same contact - follow CRM-5598 rules.
2354 *
64bd5a0e
TO
2355 * @param int $mainContactId
2356 * Contact id of main contact record.
2357 * @param int $mainCaseId
2358 * Case id of main case record.
2359 * @param int $otherContactId
2360 * Contact id of record which is going to merge.
2361 * @param int $otherCaseId
2362 * Case id of record which is going to merge.
77b97be7
EM
2363 *
2364 * @param bool $changeClient
6a488035 2365 *
df8d3074 2366 * @return int|NULL
6a488035 2367 */
a130e045 2368 public static function mergeCases(
28d4d481
TO
2369 $mainContactId, $mainCaseId = NULL, $otherContactId = NULL,
2370 $otherCaseId = NULL, $changeClient = FALSE) {
6a488035
TO
2371 $moveToTrash = TRUE;
2372
2373 $duplicateContacts = FALSE;
2374 if ($mainContactId && $otherContactId &&
2375 $mainContactId != $otherContactId
2376 ) {
2377 $duplicateContacts = TRUE;
2378 }
2379
2380 $duplicateCases = FALSE;
2381 if ($mainCaseId && $otherCaseId &&
2382 $mainCaseId != $otherCaseId
2383 ) {
2384 $duplicateCases = TRUE;
2385 }
2386
2387 $mainCaseIds = array();
2388 if (!$duplicateContacts && !$duplicateCases) {
2389 return $mainCaseIds;
2390 }
2391
2392 $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name');
2393 $activityStatuses = CRM_Core_PseudoConstant::activityStatus('name');
e7e657f0 2394 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
9e74e3ce 2395 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
2396 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
2397 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
8ef12e64 2398
6a488035
TO
2399 $processCaseIds = array($otherCaseId);
2400 if ($duplicateContacts && !$duplicateCases) {
2401 if ($changeClient) {
2402 $processCaseIds = array($mainCaseId);
2403 }
2404 else {
2405 //get all case ids for other contact.
2406 $processCaseIds = self::retrieveCaseIdsByContactId($otherContactId, TRUE);
2407 }
2408 if (!is_array($processCaseIds)) {
2409 return;
2410 }
2411 }
2412
2413 $session = CRM_Core_Session::singleton();
2414 $currentUserId = $session->get('userID');
2415
02094cdb
JJ
2416 CRM_Utils_Hook::pre_case_merge($mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient);
2417
6a488035
TO
2418 // copy all cases and connect to main contact id.
2419 foreach ($processCaseIds as $otherCaseId) {
2420 if ($duplicateContacts) {
2421 $mainCase = CRM_Core_DAO::copyGeneric('CRM_Case_DAO_Case', array('id' => $otherCaseId));
2422 $mainCaseId = $mainCase->id;
2423 if (!$mainCaseId) {
2424 continue;
2425 }
8bd86283
DG
2426
2427 // CRM-11662 Copy Case custom data
2428 $extends = array('case');
2429 $groupTree = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, NULL, $extends);
2430 if ($groupTree) {
2431 foreach ($groupTree as $groupID => $group) {
2432 $table[$groupTree[$groupID]['table_name']] = array('entity_id');
2433 foreach ($group['fields'] as $fieldID => $field) {
2434 $table[$groupTree[$groupID]['table_name']][] = $groupTree[$groupID]['fields'][$fieldID]['column_name'];
2435 }
2436 }
2437
2438 foreach ($table as $tableName => $tableColumns) {
e96f025a 2439 $insert = 'INSERT INTO ' . $tableName . ' (' . implode(', ', $tableColumns) . ') ';
8bd86283 2440 $tableColumns[0] = $mainCaseId;
e96f025a 2441 $select = 'SELECT ' . implode(', ', $tableColumns);
2442 $from = ' FROM ' . $tableName;
2443 $where = " WHERE {$tableName}.entity_id = {$otherCaseId}";
2444 $query = $insert . $select . $from . $where;
2445 $dao = CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
8bd86283
DG
2446 }
2447 }
e96f025a 2448
6a488035 2449 $mainCase->free();
e96f025a 2450
6a488035
TO
2451 $mainCaseIds[] = $mainCaseId;
2452 //insert record for case contact.
2453 $otherCaseContact = new CRM_Case_DAO_CaseContact();
2454 $otherCaseContact->case_id = $otherCaseId;
2455 $otherCaseContact->find();
2456 while ($otherCaseContact->fetch()) {
2457 $mainCaseContact = new CRM_Case_DAO_CaseContact();
2458 $mainCaseContact->case_id = $mainCaseId;
2459 $mainCaseContact->contact_id = $otherCaseContact->contact_id;
2460 if ($mainCaseContact->contact_id == $otherContactId) {
2461 $mainCaseContact->contact_id = $mainContactId;
2462 }
2463 //avoid duplicate object.
2464 if (!$mainCaseContact->find(TRUE)) {
2465 $mainCaseContact->save();
2466 }
2467 $mainCaseContact->free();
2468 }
2469 $otherCaseContact->free();
2470 }
2471 elseif (!$otherContactId) {
2472 $otherContactId = $mainContactId;
2473 }
2474
2475 if (!$mainCaseId || !$otherCaseId ||
2476 !$mainContactId || !$otherContactId
2477 ) {
2478 continue;
2479 }
2480
2481 // get all activities for other case.
2482 $otherCaseActivities = array();
2483 CRM_Core_DAO::commonRetrieveAll('CRM_Case_DAO_CaseActivity', 'case_id', $otherCaseId, $otherCaseActivities);
2484
2485 //for duplicate cases do not process singleton activities.
2486 $otherActivityIds = $singletonActivityIds = array();
2487 foreach ($otherCaseActivities as $caseActivityId => $otherIds) {
2488 $otherActId = CRM_Utils_Array::value('activity_id', $otherIds);
2489 if (!$otherActId || in_array($otherActId, $otherActivityIds)) {
2490 continue;
2491 }
2492 $otherActivityIds[] = $otherActId;
2493 }
2494 if ($duplicateCases) {
2495 if ($openCaseType = array_search('Open Case', $activityTypes)) {
2496 $sql = "
2497SELECT id
2498 FROM civicrm_activity
2499 WHERE activity_type_id = $openCaseType
2500 AND id IN ( " . implode(',', array_values($otherActivityIds)) . ');';
2501 $dao = CRM_Core_DAO::executeQuery($sql);
2502 while ($dao->fetch()) {
2503 $singletonActivityIds[] = $dao->id;
2504 }
2505 $dao->free();
2506 }
2507 }
2508
2509 // migrate all activities and connect to main contact.
2510 $copiedActivityIds = $activityMappingIds = array();
2511 sort($otherActivityIds);
2512 foreach ($otherActivityIds as $otherActivityId) {
2513
2514 //for duplicate cases -
2515 //do not migrate singleton activities.
2516 if (!$otherActivityId || in_array($otherActivityId, $singletonActivityIds)) {
2517 continue;
2518 }
2519
2520 //migrate activity record.
2521 $otherActivity = new CRM_Activity_DAO_Activity();
2522 $otherActivity->id = $otherActivityId;
2523 if (!$otherActivity->find(TRUE)) {
2524 continue;
2525 }
2526
2527 $mainActVals = array();
2528 $mainActivity = new CRM_Activity_DAO_Activity();
2529 CRM_Core_DAO::storeValues($otherActivity, $mainActVals);
2530 $mainActivity->copyValues($mainActVals);
2531 $mainActivity->id = NULL;
2532 $mainActivity->activity_date_time = CRM_Utils_Date::isoToMysql($otherActivity->activity_date_time);
6a488035
TO
2533 $mainActivity->source_record_id = CRM_Utils_Array::value($mainActivity->source_record_id,
2534 $activityMappingIds
2535 );
2536
2537 $mainActivity->original_id = CRM_Utils_Array::value($mainActivity->original_id,
2538 $activityMappingIds
2539 );
2540
2541 $mainActivity->parent_id = CRM_Utils_Array::value($mainActivity->parent_id,
2542 $activityMappingIds
2543 );
2544 $mainActivity->save();
2545 $mainActivityId = $mainActivity->id;
2546 if (!$mainActivityId) {
2547 continue;
2548 }
2549
2550 $activityMappingIds[$otherActivityId] = $mainActivityId;
4322672b 2551 // insert log of all activities
6a488035
TO
2552 CRM_Activity_BAO_Activity::logActivityAction($mainActivity);
2553
2554 $otherActivity->free();
2555 $mainActivity->free();
2556 $copiedActivityIds[] = $otherActivityId;
2557
2558 //create case activity record.
2559 $mainCaseActivity = new CRM_Case_DAO_CaseActivity();
2560 $mainCaseActivity->case_id = $mainCaseId;
2561 $mainCaseActivity->activity_id = $mainActivityId;
2562 $mainCaseActivity->save();
2563 $mainCaseActivity->free();
2564
4322672b 2565 //migrate source activity.
2566 $otherSourceActivity = new CRM_Activity_DAO_ActivityContact();
2567 $otherSourceActivity->activity_id = $otherActivityId;
2568 $otherSourceActivity->record_type_id = $sourceID;
2569 $otherSourceActivity->find();
2570 while ($otherSourceActivity->fetch()) {
2571 $mainActivitySource = new CRM_Activity_DAO_ActivityContact();
2572 $mainActivitySource->record_type_id = $sourceID;
2573 $mainActivitySource->activity_id = $mainActivityId;
2574 $mainActivitySource->contact_id = $otherSourceActivity->contact_id;
2575 if ($mainActivitySource->contact_id == $otherContactId) {
2576 $mainActivitySource->contact_id = $mainContactId;
2577 }
2578 //avoid duplicate object.
2579 if (!$mainActivitySource->find(TRUE)) {
2580 $mainActivitySource->save();
2581 }
2582 $mainActivitySource->free();
2583 }
2584 $otherSourceActivity->free();
2585
6a488035 2586 //migrate target activities.
4e3d3cfc 2587 $otherTargetActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2588 $otherTargetActivity->activity_id = $otherActivityId;
9e74e3ce 2589 $otherTargetActivity->record_type_id = $targetID;
6a488035
TO
2590 $otherTargetActivity->find();
2591 while ($otherTargetActivity->fetch()) {
4e3d3cfc 2592 $mainActivityTarget = new CRM_Activity_DAO_ActivityContact();
9e74e3ce 2593 $mainActivityTarget->record_type_id = $targetID;
6a488035 2594 $mainActivityTarget->activity_id = $mainActivityId;
00bf7e59 2595 $mainActivityTarget->contact_id = $otherTargetActivity->contact_id;
2596 if ($mainActivityTarget->contact_id == $otherContactId) {
2597 $mainActivityTarget->contact_id = $mainContactId;
6a488035
TO
2598 }
2599 //avoid duplicate object.
2600 if (!$mainActivityTarget->find(TRUE)) {
2601 $mainActivityTarget->save();
2602 }
2603 $mainActivityTarget->free();
2604 }
2605 $otherTargetActivity->free();
2606
2607 //migrate assignee activities.
4e3d3cfc 2608 $otherAssigneeActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2609 $otherAssigneeActivity->activity_id = $otherActivityId;
9e74e3ce 2610 $otherAssigneeActivity->record_type_id = $assigneeID;
6a488035
TO
2611 $otherAssigneeActivity->find();
2612 while ($otherAssigneeActivity->fetch()) {
4e3d3cfc 2613 $mainAssigneeActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2614 $mainAssigneeActivity->activity_id = $mainActivityId;
9e74e3ce 2615 $mainAssigneeActivity->record_type_id = $assigneeID;
00bf7e59 2616 $mainAssigneeActivity->contact_id = $otherAssigneeActivity->contact_id;
2617 if ($mainAssigneeActivity->contact_id == $otherContactId) {
2618 $mainAssigneeActivity->contact_id = $mainContactId;
6a488035
TO
2619 }
2620 //avoid duplicate object.
2621 if (!$mainAssigneeActivity->find(TRUE)) {
2622 $mainAssigneeActivity->save();
2623 }
2624 $mainAssigneeActivity->free();
2625 }
2626 $otherAssigneeActivity->free();
8c31eef5
D
2627
2628 // copy custom fields and attachments
4322672b 2629 $aparams = array(
2630 'activityID' => $otherActivityId,
2631 'mainActivityId' => $mainActivityId,
2632 );
8c31eef5 2633 CRM_Activity_BAO_Activity::copyExtendedActivityData($aparams);
6a488035
TO
2634 }
2635
2636 //copy case relationship.
2637 if ($duplicateContacts) {
2638 //migrate relationship records.
2639 $otherRelationship = new CRM_Contact_DAO_Relationship();
2640 $otherRelationship->case_id = $otherCaseId;
2641 $otherRelationship->find();
2642 $otherRelationshipIds = array();
2643 while ($otherRelationship->fetch()) {
2644 $otherRelVals = array();
2645 $updateOtherRel = FALSE;
2646 CRM_Core_DAO::storeValues($otherRelationship, $otherRelVals);
2647
2648 $mainRelationship = new CRM_Contact_DAO_Relationship();
2649 $mainRelationship->copyValues($otherRelVals);
2650 $mainRelationship->id = NULL;
2651 $mainRelationship->case_id = $mainCaseId;
2652 if ($mainRelationship->contact_id_a == $otherContactId) {
2653 $updateOtherRel = TRUE;
2654 $mainRelationship->contact_id_a = $mainContactId;
2655 }
2656
2657 //case creator change only when we merge user contact.
2658 if ($mainRelationship->contact_id_b == $otherContactId) {
2659 //do not change creator for change client.
2660 if (!$changeClient) {
2661 $updateOtherRel = TRUE;
2662 $mainRelationship->contact_id_b = ($currentUserId) ? $currentUserId : $mainContactId;
2663 }
2664 }
2665 $mainRelationship->end_date = CRM_Utils_Date::isoToMysql($otherRelationship->end_date);
2666 $mainRelationship->start_date = CRM_Utils_Date::isoToMysql($otherRelationship->start_date);
2667
2668 //avoid duplicate object.
2669 if (!$mainRelationship->find(TRUE)) {
2670 $mainRelationship->save();
2671 }
2672 $mainRelationship->free();
2673
2674 //get the other relationship ids to update end date.
2675 if ($updateOtherRel) {
2676 $otherRelationshipIds[$otherRelationship->id] = $otherRelationship->id;
2677 }
2678 }
2679 $otherRelationship->free();
2680
2681 //update other relationships end dates
2682 if (!empty($otherRelationshipIds)) {
2683 $sql = 'UPDATE civicrm_relationship
2684 SET end_date = CURDATE()
2685 WHERE id IN ( ' . implode(',', $otherRelationshipIds) . ')';
2686 CRM_Core_DAO::executeQuery($sql);
2687 }
2688 }
2689
2690 //move other case to trash.
2691 $mergeCase = self::deleteCase($otherCaseId, $moveToTrash);
2692 if (!$mergeCase) {
2693 continue;
2694 }
2695
2696 $mergeActSubject = $mergeActSubjectDetails = $mergeActType = '';
2697 if ($changeClient) {
2698 $mainContactDisplayName = CRM_Contact_BAO_Contact::displayName($mainContactId);
2699 $otherContactDisplayName = CRM_Contact_BAO_Contact::displayName($otherContactId);
2700
2701 $mergeActType = array_search('Reassigned Case', $activityTypes);
2702 $mergeActSubject = ts("Case %1 reassigned client from %2 to %3. New Case ID is %4.",
2703 array(
e96f025a 2704 1 => $otherCaseId,
2705 2 => $otherContactDisplayName,
2706 3 => $mainContactDisplayName,
21dfd5f5 2707 4 => $mainCaseId,
6a488035
TO
2708 )
2709 );
2710 }
2711 elseif ($duplicateContacts) {
2712 $mergeActType = array_search('Merge Case', $activityTypes);
2713 $mergeActSubject = ts("Case %1 copied from contact id %2 to contact id %3 via merge. New Case ID is %4.",
2714 array(
e96f025a 2715 1 => $otherCaseId,
2716 2 => $otherContactId,
2717 3 => $mainContactId,
21dfd5f5 2718 4 => $mainCaseId,
6a488035
TO
2719 )
2720 );
2721 }
2722 else {
2723 $mergeActType = array_search('Merge Case', $activityTypes);
2724 $mergeActSubject = ts("Case %1 merged into case %2", array(1 => $otherCaseId, 2 => $mainCaseId));
2725 if (!empty($copiedActivityIds)) {
2726 $sql = '
2727SELECT id, subject, activity_date_time, activity_type_id
2728FROM civicrm_activity
2729WHERE id IN (' . implode(',', $copiedActivityIds) . ')';
2730 $dao = CRM_Core_DAO::executeQuery($sql);
2731 while ($dao->fetch()) {
2732 $mergeActSubjectDetails .= "{$dao->activity_date_time} :: {$activityTypes[$dao->activity_type_id]}";
2733 if ($dao->subject) {
2734 $mergeActSubjectDetails .= " :: {$dao->subject}";
2735 }
2736 $mergeActSubjectDetails .= "<br />";
2737 }
2738 }
2739 }
2740
8c677b07 2741 //Create merge activity record. Source for merge activity is the logged in user's contact ID ($currentUserId).
6a488035
TO
2742 $activityParams = array(
2743 'subject' => $mergeActSubject,
2744 'details' => $mergeActSubjectDetails,
2745 'status_id' => array_search('Completed', $activityStatuses),
2746 'activity_type_id' => $mergeActType,
8c677b07 2747 'source_contact_id' => $currentUserId,
6a488035
TO
2748 'activity_date_time' => date('YmdHis'),
2749 );
2750
2751 $mergeActivity = CRM_Activity_BAO_Activity::create($activityParams);
2752 $mergeActivityId = $mergeActivity->id;
2753 if (!$mergeActivityId) {
2754 continue;
2755 }
2756 $mergeActivity->free();
2757
2758 //connect merge activity to case.
2759 $mergeCaseAct = array(
2760 'case_id' => $mainCaseId,
2761 'activity_id' => $mergeActivityId,
2762 );
2763
2764 self::processCaseActivity($mergeCaseAct);
2765 }
02094cdb
JJ
2766
2767 CRM_Utils_Hook::post_case_merge($mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient);
2768
6a488035
TO
2769 return $mainCaseIds;
2770 }
2771
2772 /**
2773 * Validate contact permission for
2774 * edit/view on activity record and build links.
2775 *
64bd5a0e
TO
2776 * @param array $tplParams
2777 * Params to be sent to template for sending email.
2778 * @param array $activityParams
2779 * Info of the activity.
6a488035 2780 */
00be9182 2781 public static function buildPermissionLinks(&$tplParams, $activityParams) {
6a488035
TO
2782 $activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityParams['source_record_id'],
2783 'activity_type_id', 'id'
2784 );
2785
a7488080 2786 if (!empty($tplParams['isCaseActivity'])) {
6a488035
TO
2787 $tplParams['editActURL'] = CRM_Utils_System::url('civicrm/case/activity',
2788 "reset=1&cid={$activityParams['target_id']}&caseid={$activityParams['case_id']}&action=update&id={$activityParams['source_record_id']}", TRUE
2789 );
2790
2791 $tplParams['viewActURL'] = CRM_Utils_System::url('civicrm/case/activity/view',
2792 "reset=1&aid={$activityParams['source_record_id']}&cid={$activityParams['target_id']}&caseID={$activityParams['case_id']}", TRUE
2793 );
2794
2795 $tplParams['manageCaseURL'] = CRM_Utils_System::url('civicrm/contact/view/case',
2796 "reset=1&id={$activityParams['case_id']}&cid={$activityParams['target_id']}&action=view&context=home", TRUE
2797 );
2798 }
2799 else {
2800 $tplParams['editActURL'] = CRM_Utils_System::url('civicrm/contact/view/activity',
2801 "atype=$activityTypeId&action=update&reset=1&id={$activityParams['source_record_id']}&cid={$tplParams['contact']['contact_id']}&context=activity", TRUE
2802 );
2803
2804 $tplParams['viewActURL'] = CRM_Utils_System::url('civicrm/contact/view/activity',
2805 "atype=$activityTypeId&action=view&reset=1&id={$activityParams['source_record_id']}&cid={$tplParams['contact']['contact_id']}&context=activity", TRUE
2806 );
2807 }
2808 }
2809
2810 /**
2811 * Validate contact permission for
2812 * given operation on activity record.
2813 *
64bd5a0e
TO
2814 * @param int $activityId
2815 * Activity record id.
2816 * @param string $operation
2817 * User operation.
2818 * @param int $actTypeId
2819 * Activity type id.
2820 * @param int $contactId
2821 * Contact id/if not pass consider logged in.
2822 * @param bool $checkComponent
2823 * Do we need to check component enabled.
6a488035 2824 *
a130e045 2825 * @return bool
6a488035 2826 */
00be9182 2827 public static function checkPermission($activityId, $operation, $actTypeId = NULL, $contactId = NULL, $checkComponent = TRUE) {
6a488035
TO
2828 $allow = FALSE;
2829 if (!$actTypeId && $activityId) {
2830 $actTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id');
2831 }
2832
2833 if (!$activityId || !$operation || !$actTypeId) {
2834 return $allow;
2835 }
2836
2837 //do check for civicase component enabled.
077dbf5e
CW
2838 if ($checkComponent && !self::enabled()) {
2839 return $allow;
6a488035
TO
2840 }
2841
2842 //do check for cases.
2843 $caseActOperations = array(
2844 'File On Case',
2845 'Link Cases',
2846 'Move To Case',
2847 'Copy To Case',
2848 );
2849
2850 if (in_array($operation, $caseActOperations)) {
2851 static $unclosedCases;
2852 if (!is_array($unclosedCases)) {
2853 $unclosedCases = self::getUnclosedCases();
2854 }
2855 if ($operation == 'File On Case') {
2856 $allow = (empty($unclosedCases)) ? FALSE : TRUE;
2857 }
2858 else {
2859 $allow = (count($unclosedCases) > 1) ? TRUE : FALSE;
2860 }
2861 }
2862
2863 $actionOperations = array('view', 'edit', 'delete');
2864 if (in_array($operation, $actionOperations)) {
2865
2866 //do cache when user has non/supper permission.
2867 static $allowOperations;
2868
2869 if (!is_array($allowOperations) ||
2870 !array_key_exists($operation, $allowOperations)
2871 ) {
2872
2873 if (!$contactId) {
2874 $session = CRM_Core_Session::singleton();
2875 $contactId = $session->get('userID');
2876 }
2877
2878 //check for permissions.
2879 $permissions = array(
2880 'view' => array(
2881 'access my cases and activities',
2882 'access all cases and activities',
2883 ),
2884 'edit' => array(
2885 'access my cases and activities',
2886 'access all cases and activities',
2887 ),
2888 'delete' => array('delete activities'),
2889 );
2890
2891 //check for core permission.
2892 $hasPermissions = array();
2893 $checkPermissions = CRM_Utils_Array::value($operation, $permissions);
2894 if (is_array($checkPermissions)) {
2895 foreach ($checkPermissions as $per) {
2896 if (CRM_Core_Permission::check($per)) {
2897 $hasPermissions[$operation][] = $per;
2898 }
2899 }
2900 }
2901
2902 //has permissions.
2903 if (!empty($hasPermissions)) {
2904 //need to check activity object specific.
2905 if (in_array($operation, array(
e96f025a 2906 'view',
21dfd5f5 2907 'edit',
e96f025a 2908 ))
2909 ) {
6a488035
TO
2910 //do we have supper permission.
2911 if (in_array('access all cases and activities', $hasPermissions[$operation])) {
2912 $allowOperations[$operation] = $allow = TRUE;
2913 }
2914 else {
2915 //user has only access to my cases and activity.
2916 //here object specific permmions come in picture.
2917
2918 //edit - contact must be source or assignee
2919 //view - contact must be source/assignee/target
2920 $isTarget = $isAssignee = $isSource = FALSE;
499e172c 2921 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
4322672b 2922 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
2923 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
2924 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
6a488035 2925
4e3d3cfc 2926 $target = new CRM_Activity_DAO_ActivityContact();
9e74e3ce 2927 $target->record_type_id = $targetID;
6a488035 2928 $target->activity_id = $activityId;
00bf7e59 2929 $target->contact_id = $contactId;
6a488035
TO
2930 if ($target->find(TRUE)) {
2931 $isTarget = TRUE;
2932 }
2933
4e3d3cfc 2934 $assignee = new CRM_Activity_DAO_ActivityContact();
6a488035 2935 $assignee->activity_id = $activityId;
9e74e3ce 2936 $assignee->record_type_id = $assigneeID;
00bf7e59 2937 $assignee->contact_id = $contactId;
6a488035
TO
2938 if ($assignee->find(TRUE)) {
2939 $isAssignee = TRUE;
2940 }
2941
4322672b 2942 $source = new CRM_Activity_DAO_ActivityContact();
2943 $source->activity_id = $activityId;
2944 $source->record_type_id = $sourceID;
2945 $source->contact_id = $contactId;
2946 if ($source->find(TRUE)) {
6a488035
TO
2947 $isSource = TRUE;
2948 }
2949
2950 if ($operation == 'edit') {
2951 if ($isAssignee || $isSource) {
2952 $allow = TRUE;
2953 }
2954 }
2955 if ($operation == 'view') {
2956 if ($isTarget || $isAssignee || $isSource) {
2957 $allow = TRUE;
2958 }
2959 }
2960 }
2961 }
2962 elseif (is_array($hasPermissions[$operation])) {
2963 $allowOperations[$operation] = $allow = TRUE;
2964 }
2965 }
2966 else {
2967 //contact do not have permission.
2968 $allowOperations[$operation] = FALSE;
2969 }
2970 }
2971 else {
2972 //use cache.
2973 //here contact might have supper/non permission.
2974 $allow = $allowOperations[$operation];
2975 }
2976 }
2977
2978 //do further only when operation is granted.
2979 if ($allow) {
2980 $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name');
2981
2982 //get the activity type name.
2983 $actTypeName = CRM_Utils_Array::value($actTypeId, $activityTypes);
2984
2985 //do not allow multiple copy / edit action.
e96f025a 2986 $singletonNames = array(
2987 'Open Case',
2988 'Reassigned Case',
2989 'Merge Case',
2990 'Link Cases',
2991 'Assign Case Role',
2992 'Email',
21dfd5f5 2993 'Inbound Email',
e96f025a 2994 );
6a488035
TO
2995
2996 //do not allow to delete these activities, CRM-4543
2997 $doNotDeleteNames = array('Open Case', 'Change Case Type', 'Change Case Status', 'Change Case Start Date');
2998
2999 //allow edit operation.
3000 $allowEditNames = array('Open Case');
3001
3002 // do not allow File on Case
e96f025a 3003 $doNotFileNames = array(
3004 'Open Case',
3005 'Change Case Type',
3006 'Change Case Status',
3007 'Change Case Start Date',
3008 'Reassigned Case',
3009 'Merge Case',
3010 'Link Cases',
21dfd5f5 3011 'Assign Case Role',
e96f025a 3012 );
6a488035
TO
3013
3014 if (in_array($actTypeName, $singletonNames)) {
3015 $allow = FALSE;
3016 if ($operation == 'File On Case') {
3017 $allow = (in_array($actTypeName, $doNotFileNames)) ? FALSE : TRUE;
3018 }
3019 if (in_array($operation, $actionOperations)) {
3020 $allow = TRUE;
3021 if ($operation == 'edit') {
3022 $allow = (in_array($actTypeName, $allowEditNames)) ? TRUE : FALSE;
3023 }
3024 elseif ($operation == 'delete') {
3025 $allow = (in_array($actTypeName, $doNotDeleteNames)) ? FALSE : TRUE;
3026 }
3027 }
3028 }
3029 if ($allow && ($operation == 'delete') &&
3030 in_array($actTypeName, $doNotDeleteNames)
3031 ) {
3032 $allow = FALSE;
3033 }
3034
3035 if ($allow && ($operation == 'File On Case') &&
3036 in_array($actTypeName, $doNotFileNames)
3037 ) {
3038 $allow = FALSE;
3039 }
3040
3041 //check settings file for masking actions
3042 //on the basis the activity types
3043 //hide Edit link if activity type is NOT editable
3044 //(special case activities).CRM-5871
3045 if ($allow && in_array($operation, $actionOperations)) {
3046 static $actionFilter = array();
3047 if (!array_key_exists($operation, $actionFilter)) {
3048 $xmlProcessor = new CRM_Case_XMLProcessor_Process();
3049 $actionFilter[$operation] = $xmlProcessor->get('Settings', 'ActivityTypes', FALSE, $operation);
3050 }
3051 if (array_key_exists($operation, $actionFilter[$operation]) &&
3052 in_array($actTypeId, $actionFilter[$operation][$operation])
3053 ) {
3054 $allow = FALSE;
3055 }
3056 }
3057 }
3058
3059 return $allow;
3060 }
3061
3062 /**
100fef9d 3063 * Since we drop 'access CiviCase', allow access
6a488035
TO
3064 * if user has 'access my cases and activities'
3065 * or 'access all cases and activities'
3066 */
00be9182 3067 public static function accessCiviCase() {
077dbf5e 3068 if (!self::enabled()) {
6a488035
TO
3069 return FALSE;
3070 }
3071
3072 if (CRM_Core_Permission::check('access my cases and activities') ||
3073 CRM_Core_Permission::check('access all cases and activities')
3074 ) {
3075 return TRUE;
3076 }
3077
3078 return FALSE;
3079 }
3080
3081 /**
d2e5d2ce 3082 * Verify user has permission to access a case.
077dbf5e
CW
3083 *
3084 * @param int $caseId
64bd5a0e
TO
3085 * @param bool $denyClosed
3086 * Set TRUE if one wants closed cases to be treated as inaccessible.
077dbf5e
CW
3087 *
3088 * @return bool
3089 */
00be9182 3090 public static function accessCase($caseId, $denyClosed = TRUE) {
077dbf5e
CW
3091 if (!$caseId || !self::enabled()) {
3092 return FALSE;
3093 }
3094
3095 // This permission always has access
3096 if (CRM_Core_Permission::check('access all cases and activities')) {
3097 return TRUE;
3098 }
3099
3100 // This permission is required at minimum
3101 if (!CRM_Core_Permission::check('access my cases and activities')) {
3102 return FALSE;
3103 }
3104
546abeb1
TO
3105 $session = CRM_Core_Session::singleton();
3106 $userID = CRM_Utils_Type::validate($session->get('userID'), 'Positive');
3107 $caseId = CRM_Utils_Type::validate($caseId, 'Positive');
3108
3109 $condition = " AND civicrm_case.is_deleted = 0 ";
3110 $condition .= " AND case_relationship.contact_id_b = {$userID} ";
3111 $condition .= " AND civicrm_case.id = {$caseId}";
3112
3113 if ($denyClosed) {
3114 $closedId = CRM_Core_OptionGroup::getValue('case_status', 'Closed', 'name');
3115 $condition .= " AND civicrm_case.status_id != $closedId";
3116 }
3117
3118 // We don't actually care about activities in the case, but the underlying
3119 // query is verbose, and this allows us to share the basic query with
3120 // getCases(). $type=='any' means that activities will be left-joined.
3121 $query = self::getCaseActivityQuery('any', $userID, $condition);
3122 $queryParams = array();
3123 $dao = CRM_Core_DAO::executeQuery($query,
3124 $queryParams
3125 );
077dbf5e 3126
546abeb1 3127 return (bool) $dao->fetch();
077dbf5e
CW
3128 }
3129
6a488035 3130 /**
d2e5d2ce 3131 * Check whether activity is a case Activity.
6a488035 3132 *
64bd5a0e
TO
3133 * @param int $activityID
3134 * Activity id.
6a488035 3135 *
a130e045 3136 * @return bool
6a488035 3137 */
00be9182 3138 public static function isCaseActivity($activityID) {
6a488035
TO
3139 $isCaseActivity = FALSE;
3140 if ($activityID) {
3141 $params = array(1 => array($activityID, 'Integer'));
3142 $query = "SELECT id FROM civicrm_case_activity WHERE activity_id = %1";
3143 if (CRM_Core_DAO::singleValueQuery($query, $params)) {
3144 $isCaseActivity = TRUE;
3145 }
3146 }
3147
3148 return $isCaseActivity;
3149 }
3150
3151 /**
d2e5d2ce 3152 * Get all the case type ids currently in use.
6a488035 3153 *
a6c01b45 3154 * @return array
6a488035 3155 */
00be9182 3156 public static function getUsedCaseType() {
6a488035
TO
3157 static $caseTypeIds;
3158
3159 if (!is_array($caseTypeIds)) {
3160 $query = "SELECT DISTINCT( civicrm_case.case_type_id ) FROM civicrm_case";
3161
3162 $dao = CRM_Core_DAO::executeQuery($query);
3163 $caseTypeIds = array();
3164 while ($dao->fetch()) {
3165 $typeId = explode(CRM_Core_DAO::VALUE_SEPARATOR,
3166 $dao->case_type_id
3167 );
3168 $caseTypeIds[] = $typeId[1];
3169 }
3170 }
3171
3172 return $caseTypeIds;
3173 }
3174
3175 /**
d2e5d2ce 3176 * Get all the case status ids currently in use.
6a488035 3177 *
a6c01b45 3178 * @return array
6a488035 3179 */
00be9182 3180 public static function getUsedCaseStatuses() {
6a488035
TO
3181 static $caseStatusIds;
3182
3183 if (!is_array($caseStatusIds)) {
3184 $query = "SELECT DISTINCT( civicrm_case.status_id ) FROM civicrm_case";
3185
3186 $dao = CRM_Core_DAO::executeQuery($query);
3187 $caseStatusIds = array();
3188 while ($dao->fetch()) {
3189 $caseStatusIds[] = $dao->status_id;
3190 }
3191 }
3192
3193 return $caseStatusIds;
3194 }
3195
3196 /**
d2e5d2ce 3197 * Get all the encounter medium ids currently in use.
72b3a70c 3198 *
6a488035
TO
3199 * @return array
3200 */
00be9182 3201 public static function getUsedEncounterMediums() {
6a488035
TO
3202 static $mediumIds;
3203
3204 if (!is_array($mediumIds)) {
3205 $query = "SELECT DISTINCT( civicrm_activity.medium_id ) FROM civicrm_activity";
3206
3207 $dao = CRM_Core_DAO::executeQuery($query);
3208 $mediumIds = array();
3209 while ($dao->fetch()) {
3210 $mediumIds[] = $dao->medium_id;
3211 }
3212 }
3213
3214 return $mediumIds;
3215 }
3216
3217 /**
100fef9d 3218 * Check case configuration.
6a488035 3219 *
100fef9d 3220 * @param int $contactId
77b97be7 3221 *
a6c01b45 3222 * @return array
6a488035 3223 */
00be9182 3224 public static function isCaseConfigured($contactId = NULL) {
6a488035
TO
3225 $configured = array_fill_keys(array('configured', 'allowToAddNewCase', 'redirectToCaseAdmin'), FALSE);
3226
3227 //lets check for case configured.
3228 $allCasesCount = CRM_Case_BAO_Case::caseCount(NULL, FALSE);
3229 $configured['configured'] = ($allCasesCount) ? TRUE : FALSE;
3230 if (!$configured['configured']) {
3231 //do check for case type and case status.
0372ffa2 3232 $caseTypes = CRM_Case_PseudoConstant::caseType('title', FALSE);
6a488035
TO
3233 if (!empty($caseTypes)) {
3234 $configured['configured'] = TRUE;
3235 if (!$configured['configured']) {
3236 $caseStatuses = CRM_Case_PseudoConstant::caseStatus('label', FALSE);
3237 if (!empty($caseStatuses)) {
3238 $configured['configured'] = TRUE;
3239 }
3240 }
3241 }
3242 }
3243 if ($configured['configured']) {
3244 //do check for active case type and case status.
3245 $caseTypes = CRM_Case_PseudoConstant::caseType();
3246 if (!empty($caseTypes)) {
3247 $caseStatuses = CRM_Case_PseudoConstant::caseStatus();
3248 if (!empty($caseStatuses)) {
3249 $configured['allowToAddNewCase'] = TRUE;
3250 }
3251 }
3252
3253 //do we need to redirect user to case admin.
3254 if (!$configured['allowToAddNewCase'] && $contactId) {
3255 //check for current contact case count.
3256 $currentContatCasesCount = CRM_Case_BAO_Case::caseCount($contactId);
3257 //redirect user to case admin page.
3258 if (!$currentContatCasesCount) {
3259 $configured['redirectToCaseAdmin'] = TRUE;
3260 }
3261 }
3262 }
3263
3264 return $configured;
3265 }
3266
d6f468d3 3267 /**
d2e5d2ce 3268 * Used during case component enablement and during ugprade.
72b3a70c
CW
3269 *
3270 * @return bool
6a488035 3271 */
00be9182 3272 public static function createCaseViews() {
6a4257d4 3273 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
a19fc402
TO
3274 $dao = new CRM_Core_DAO();
3275
6a488035 3276 $sql = self::createCaseViewsQuery('upcoming');
6a488035
TO
3277 $dao->query($sql);
3278 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
3279 return FALSE;
3280 }
3281
3282 // Above error doesn't get caught?
3283 $doublecheck = $dao->singleValueQuery("SELECT count(id) FROM civicrm_view_case_activity_upcoming");
3284 if (is_null($doublecheck)) {
3285 return FALSE;
3286 }
3287
3288 $sql = self::createCaseViewsQuery('recent');
6a488035
TO
3289 $dao->query($sql);
3290 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
3291 return FALSE;
3292 }
3293
3294 // Above error doesn't get caught?
3295 $doublecheck = $dao->singleValueQuery("SELECT count(id) FROM civicrm_view_case_activity_recent");
3296 if (is_null($doublecheck)) {
3297 return FALSE;
3298 }
3299
3300 return TRUE;
3301 }
3302
d6f468d3 3303 /**
100fef9d 3304 * Helper function, also used by the upgrade in case of error
72b3a70c 3305 *
cde2037d
EM
3306 * @param string $section
3307 *
72b3a70c 3308 * @return string
6a488035 3309 */
00be9182 3310 public static function createCaseViewsQuery($section = 'upcoming') {
6a488035
TO
3311 $sql = "";
3312 $scheduled_id = CRM_Core_OptionGroup::getValue('activity_status', 'Scheduled', 'name');
3313 switch ($section) {
3314 case 'upcoming':
3315 $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_upcoming`
3316 AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
3317 FROM civicrm_case_activity ca
3318 INNER JOIN civicrm_activity a ON ca.activity_id=a.id
3319 WHERE a.activity_date_time <= DATE_ADD( NOW(), INTERVAL 14 DAY )
3320 AND a.is_current_revision = 1 AND a.is_deleted=0 AND a.status_id = $scheduled_id";
3321 break;
3322
3323 case 'recent':
3324 $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_recent`
3325 AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
3326 FROM civicrm_case_activity ca
3327 INNER JOIN civicrm_activity a ON ca.activity_id=a.id
3328 WHERE a.activity_date_time <= NOW()
3329 AND a.activity_date_time >= DATE_SUB( NOW(), INTERVAL 14 DAY )
3330 AND a.is_current_revision = 1 AND a.is_deleted=0 AND a.status_id <> $scheduled_id";
3331 break;
3332 }
6a488035 3333 return $sql;
e96f025a 3334 }
3335
3336 /**
100fef9d 3337 * Add/copy relationships, when new client is added for a case
e96f025a 3338 *
64bd5a0e
TO
3339 * @param int $caseId
3340 * Case id.
3341 * @param int $contactId
3342 * Contact id / new client id.
e96f025a 3343 */
00be9182 3344 public static function addCaseRelationships($caseId, $contactId) {
44466d80
KJ
3345 // get the case role / relationships for the case
3346 $caseRelationships = new CRM_Contact_DAO_Relationship();
3347 $caseRelationships->case_id = $caseId;
3348 $caseRelationships->find();
3349 $relationshipTypes = array();
3350
3351 // make sure we don't add duplicate relationships of same relationship type.
3352 while ($caseRelationships->fetch() && !in_array($caseRelationships->relationship_type_id, $relationshipTypes)) {
3353 $values = array();
3354 CRM_Core_DAO::storeValues($caseRelationships, $values);
3355
3356 // add relationship for new client.
3357 $newRelationship = new CRM_Contact_DAO_Relationship();
3358 $newRelationship->copyValues($values);
3359 $newRelationship->id = NULL;
3360 $newRelationship->case_id = $caseId;
3361 $newRelationship->contact_id_a = $contactId;
3362 $newRelationship->end_date = CRM_Utils_Date::isoToMysql($caseRelationships->end_date);
3363 $newRelationship->start_date = CRM_Utils_Date::isoToMysql($caseRelationships->start_date);
3364
3365 // another check to avoid duplicate relationship, in cases where client is removed and re-added again.
3366 if (!$newRelationship->find(TRUE)) {
3367 $newRelationship->save();
3368 }
3369 $newRelationship->free();
3370
3371 // store relationship type of newly created relationship
3372 $relationshipTypes[] = $caseRelationships->relationship_type_id;
3373 }
6a488035 3374 }
14a679f1
KJ
3375
3376 /**
d2e5d2ce 3377 * Get the list of clients for a case.
14a679f1
KJ
3378 *
3379 * @param int $caseId
3380 *
a6c01b45
CW
3381 * @return array
3382 * associated array with client ids
14a679f1 3383 */
00be9182 3384 public static function getCaseClients($caseId) {
14a679f1
KJ
3385 $clients = array();
3386 $caseContact = new CRM_Case_DAO_CaseContact();
3387 $caseContact->case_id = $caseId;
3388 $caseContact->find();
3389
e96f025a 3390 while ($caseContact->fetch()) {
14a679f1
KJ
3391 $clients[] = $caseContact->contact_id;
3392 }
3393
3394 return $clients;
3395 }
16c0ec8d
CW
3396
3397 /**
3398 * Get options for a given case field.
3399 * @see CRM_Core_DAO::buildOptions
3400 *
64bd5a0e
TO
3401 * @param string $fieldName
3402 * @param string $context
408b79bf 3403 * @see CRM_Core_DAO::buildOptionsContext
64bd5a0e 3404 * @param array $props
72b3a70c 3405 * Whatever is known about this dao object.
77b97be7 3406 *
a130e045 3407 * @return array|bool
16c0ec8d
CW
3408 */
3409 public static function buildOptions($fieldName, $context = NULL, $props = array()) {
3410 $className = __CLASS__;
3411 $params = array();
3412 switch ($fieldName) {
3413 // This field is not part of this object but the api supports it
3414 case 'medium_id':
3415 $className = 'CRM_Activity_BAO_Activity';
3416 break;
3417 }
3418 return CRM_Core_PseudoConstant::get($className, $fieldName, $params, $context);
3419 }
96025800 3420
6a488035 3421}