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