Merge pull request #12001 from jitendrapurohit/core-64
[civicrm-core.git] / CRM / Case / BAO / Case.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
8c9251b3 6 | Copyright CiviCRM LLC (c) 2004-2018 |
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
8c9251b3 31 * @copyright CiviCRM LLC (c) 2004-2018
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
5757b086 1892 $caseManagerName = '---';
6a488035
TO
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()) {
5757b086 1913 $caseManagerName = sprintf('<a href="%s">%s</a>',
c00bd201 1914 CRM_Utils_System::url('civicrm/contact/view', array('cid' => $dao->casemanager_id)),
1915 $dao->casemanager
1916 );
6a488035
TO
1917 }
1918 }
5757b086 1919
1920 return $caseManagerName;
6a488035
TO
1921 }
1922
4c6ce474 1923 /**
100fef9d 1924 * @param int $contactId
4c6ce474
EM
1925 * @param bool $excludeDeleted
1926 *
eeb45e43 1927 * @return int
4c6ce474 1928 */
00be9182 1929 public static function caseCount($contactId = NULL, $excludeDeleted = TRUE) {
0a1a8b63 1930 $params = array('check_permissions' => TRUE);
6a488035 1931 if ($excludeDeleted) {
0a1a8b63 1932 $params['is_deleted'] = 0;
6a488035
TO
1933 }
1934 if ($contactId) {
0a1a8b63 1935 $params['contact_id'] = $contactId;
6a488035 1936 }
eeb45e43
CW
1937 try {
1938 return civicrm_api3('Case', 'getcount', $params);
1939 }
1940 catch (CiviCRM_API3_Exception $e) {
1941 // Lack of permissions will throw an exception
1942 return 0;
1943 }
6a488035
TO
1944 }
1945
1946 /**
6e19e2ea 1947 * Retrieve related case ids for given case.
6a488035 1948 *
6e19e2ea 1949 * @param int $caseId
64bd5a0e
TO
1950 * @param bool $excludeDeleted
1951 * Do not include deleted cases.
6a488035 1952 *
72b3a70c 1953 * @return array
6a488035 1954 */
6e19e2ea 1955 public static function getRelatedCaseIds($caseId, $excludeDeleted = TRUE) {
6a488035
TO
1956 //FIXME : do check for permissions.
1957
6e19e2ea
CW
1958 if (!$caseId) {
1959 return array();
6a488035
TO
1960 }
1961
1962 $linkActType = array_search('Link Cases',
1963 CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name')
1964 );
1965 if (!$linkActType) {
6e19e2ea 1966 return array();
6a488035
TO
1967 }
1968
1969 $whereClause = "mainCase.id = %2";
1970 if ($excludeDeleted) {
1971 $whereClause .= " AND ( relAct.is_deleted = 0 OR relAct.is_deleted IS NULL )";
1972 }
1973
6a488035
TO
1974 $query = "
1975 SELECT relCaseAct.case_id
1976 FROM civicrm_case mainCase
1977 INNER JOIN civicrm_case_activity mainCaseAct ON (mainCaseAct.case_id = mainCase.id)
1978 INNER JOIN civicrm_activity mainAct ON (mainCaseAct.activity_id = mainAct.id AND mainAct.activity_type_id = %1)
1979 INNER JOIN civicrm_case_activity relCaseAct ON (relCaseAct.activity_id = mainAct.id AND mainCaseAct.id != relCaseAct.id)
1980 INNER JOIN civicrm_activity relAct ON (relCaseAct.activity_id = relAct.id AND relAct.activity_type_id = %1)
1981 WHERE $whereClause";
1982
1983 $dao = CRM_Core_DAO::executeQuery($query, array(
1984 1 => array($linkActType, 'Integer'),
6e19e2ea 1985 2 => array($caseId, 'Integer'),
6a488035
TO
1986 ));
1987 $relatedCaseIds = array();
1988 while ($dao->fetch()) {
1989 $relatedCaseIds[$dao->case_id] = $dao->case_id;
1990 }
1991 $dao->free();
1992
6e19e2ea
CW
1993 return array_values($relatedCaseIds);
1994 }
1995
1996 /**
1997 * Retrieve related case details for given case.
1998 *
1999 * @param int $caseId
2000 * @param bool $excludeDeleted
2001 * Do not include deleted cases.
2002 *
2003 * @return array
2004 */
2005 public static function getRelatedCases($caseId, $excludeDeleted = TRUE) {
2006 $relatedCaseIds = self::getRelatedCaseIds($caseId, $excludeDeleted);
2007 $relatedCases = array();
2008
2009 if (!$relatedCaseIds) {
2010 return array();
6a488035
TO
2011 }
2012
2013 $whereClause = 'relCase.id IN ( ' . implode(',', $relatedCaseIds) . ' )';
2014 if ($excludeDeleted) {
2015 $whereClause .= " AND ( relCase.is_deleted = 0 OR relCase.is_deleted IS NULL )";
2016 }
2017
2018 //filter for permissioned cases.
2019 $filterCases = array();
2020 $doFilterCases = FALSE;
2021 if (!CRM_Core_Permission::check('access all cases and activities')) {
2022 $doFilterCases = TRUE;
5f1c8c57 2023 $filterCases = CRM_Case_BAO_Case::getCases(FALSE);
6a488035
TO
2024 }
2025
2026 //2. fetch the details of related cases.
2027 $query = "
2028 SELECT relCase.id as id,
8ffdec17 2029 civicrm_case_type.title as case_type,
6a488035 2030 client.display_name as client_name,
74b15fae
CR
2031 client.id as client_id,
2032 relCase.status_id
6a488035
TO
2033 FROM civicrm_case relCase
2034 INNER JOIN civicrm_case_contact relCaseContact ON ( relCase.id = relCaseContact.case_id )
2035 INNER JOIN civicrm_contact client ON ( client.id = relCaseContact.contact_id )
8ffdec17 2036 LEFT JOIN civicrm_case_type ON relCase.case_type_id = civicrm_case_type.id
6a488035
TO
2037 WHERE {$whereClause}";
2038
e96f025a 2039 $dao = CRM_Core_DAO::executeQuery($query);
6a488035
TO
2040 $contactViewUrl = CRM_Utils_System::url("civicrm/contact/view", "reset=1&cid=");
2041 $hasViewContact = CRM_Core_Permission::giveMeAllACLs();
74b15fae 2042 $statuses = CRM_Case_BAO_Case::buildOptions('status_id');
6a488035
TO
2043
2044 while ($dao->fetch()) {
2045 $caseView = NULL;
2046 if (!$doFilterCases || array_key_exists($dao->id, $filterCases)) {
2047 $caseViewStr = "reset=1&id={$dao->id}&cid={$dao->client_id}&action=view&context=case&selectedChild=case";
2048 $caseViewUrl = CRM_Utils_System::url("civicrm/contact/view/case", $caseViewStr);
6ce08914 2049 $caseView = "<a class='action-item no-popup crm-hover-button' href='{$caseViewUrl}'>" . ts('View Case') . "</a>";
6a488035
TO
2050 }
2051 $clientView = $dao->client_name;
2052 if ($hasViewContact) {
2053 $clientView = "<a href='{$contactViewUrl}{$dao->client_id}'>$dao->client_name</a>";
2054 }
2055
2056 $relatedCases[$dao->id] = array(
2057 'case_id' => $dao->id,
2058 'case_type' => $dao->case_type,
2059 'client_name' => $clientView,
74b15fae 2060 'case_status' => $statuses[$dao->status_id],
6a488035
TO
2061 'links' => $caseView,
2062 );
2063 }
2064 $dao->free();
2065
2066 return $relatedCases;
2067 }
2068
2069 /**
2070 * Merge two duplicate contacts' cases - follow CRM-5758 rules.
2071 *
2072 * @see CRM_Dedupe_Merger::cpTables()
2073 *
2074 * TODO: use the 3rd $sqls param to append sql statements rather than executing them here
cde2037d
EM
2075 *
2076 * @param int $mainContactId
2077 * @param int $otherContactId
6a488035 2078 */
00be9182 2079 public static function mergeContacts($mainContactId, $otherContactId) {
6a488035
TO
2080 self::mergeCases($mainContactId, NULL, $otherContactId);
2081 }
2082
2083 /**
2084 * Function perform two task.
2085 * 1. Merge two duplicate contacts cases - follow CRM-5758 rules.
2086 * 2. Merge two cases of same contact - follow CRM-5598 rules.
2087 *
64bd5a0e
TO
2088 * @param int $mainContactId
2089 * Contact id of main contact record.
2090 * @param int $mainCaseId
2091 * Case id of main case record.
2092 * @param int $otherContactId
2093 * Contact id of record which is going to merge.
2094 * @param int $otherCaseId
2095 * Case id of record which is going to merge.
77b97be7
EM
2096 *
2097 * @param bool $changeClient
6a488035 2098 *
df8d3074 2099 * @return int|NULL
6a488035 2100 */
a130e045 2101 public static function mergeCases(
28d4d481
TO
2102 $mainContactId, $mainCaseId = NULL, $otherContactId = NULL,
2103 $otherCaseId = NULL, $changeClient = FALSE) {
6a488035
TO
2104 $moveToTrash = TRUE;
2105
2106 $duplicateContacts = FALSE;
2107 if ($mainContactId && $otherContactId &&
2108 $mainContactId != $otherContactId
2109 ) {
2110 $duplicateContacts = TRUE;
2111 }
2112
2113 $duplicateCases = FALSE;
2114 if ($mainCaseId && $otherCaseId &&
2115 $mainCaseId != $otherCaseId
2116 ) {
2117 $duplicateCases = TRUE;
2118 }
2119
2120 $mainCaseIds = array();
2121 if (!$duplicateContacts && !$duplicateCases) {
2122 return $mainCaseIds;
2123 }
2124
2125 $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name');
2126 $activityStatuses = CRM_Core_PseudoConstant::activityStatus('name');
44f817d4 2127 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
9e74e3ce 2128 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
2129 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
2130 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
8ef12e64 2131
6a488035
TO
2132 $processCaseIds = array($otherCaseId);
2133 if ($duplicateContacts && !$duplicateCases) {
2134 if ($changeClient) {
2135 $processCaseIds = array($mainCaseId);
2136 }
2137 else {
2138 //get all case ids for other contact.
2139 $processCaseIds = self::retrieveCaseIdsByContactId($otherContactId, TRUE);
2140 }
2141 if (!is_array($processCaseIds)) {
2142 return;
2143 }
2144 }
2145
2146 $session = CRM_Core_Session::singleton();
2147 $currentUserId = $session->get('userID');
2148
02094cdb
JJ
2149 CRM_Utils_Hook::pre_case_merge($mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient);
2150
6a488035
TO
2151 // copy all cases and connect to main contact id.
2152 foreach ($processCaseIds as $otherCaseId) {
2153 if ($duplicateContacts) {
2154 $mainCase = CRM_Core_DAO::copyGeneric('CRM_Case_DAO_Case', array('id' => $otherCaseId));
2155 $mainCaseId = $mainCase->id;
2156 if (!$mainCaseId) {
2157 continue;
2158 }
8bd86283
DG
2159
2160 // CRM-11662 Copy Case custom data
2161 $extends = array('case');
2162 $groupTree = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, NULL, $extends);
2163 if ($groupTree) {
2164 foreach ($groupTree as $groupID => $group) {
2165 $table[$groupTree[$groupID]['table_name']] = array('entity_id');
2166 foreach ($group['fields'] as $fieldID => $field) {
2167 $table[$groupTree[$groupID]['table_name']][] = $groupTree[$groupID]['fields'][$fieldID]['column_name'];
2168 }
2169 }
2170
2171 foreach ($table as $tableName => $tableColumns) {
e96f025a 2172 $insert = 'INSERT INTO ' . $tableName . ' (' . implode(', ', $tableColumns) . ') ';
8bd86283 2173 $tableColumns[0] = $mainCaseId;
e96f025a 2174 $select = 'SELECT ' . implode(', ', $tableColumns);
2175 $from = ' FROM ' . $tableName;
2176 $where = " WHERE {$tableName}.entity_id = {$otherCaseId}";
2177 $query = $insert . $select . $from . $where;
33621c4f 2178 $dao = CRM_Core_DAO::executeQuery($query);
8bd86283
DG
2179 }
2180 }
e96f025a 2181
6a488035 2182 $mainCase->free();
e96f025a 2183
6a488035
TO
2184 $mainCaseIds[] = $mainCaseId;
2185 //insert record for case contact.
2186 $otherCaseContact = new CRM_Case_DAO_CaseContact();
2187 $otherCaseContact->case_id = $otherCaseId;
2188 $otherCaseContact->find();
2189 while ($otherCaseContact->fetch()) {
2190 $mainCaseContact = new CRM_Case_DAO_CaseContact();
2191 $mainCaseContact->case_id = $mainCaseId;
2192 $mainCaseContact->contact_id = $otherCaseContact->contact_id;
2193 if ($mainCaseContact->contact_id == $otherContactId) {
2194 $mainCaseContact->contact_id = $mainContactId;
2195 }
2196 //avoid duplicate object.
2197 if (!$mainCaseContact->find(TRUE)) {
2198 $mainCaseContact->save();
2199 }
2200 $mainCaseContact->free();
2201 }
2202 $otherCaseContact->free();
2203 }
2204 elseif (!$otherContactId) {
2205 $otherContactId = $mainContactId;
2206 }
2207
2208 if (!$mainCaseId || !$otherCaseId ||
2209 !$mainContactId || !$otherContactId
2210 ) {
2211 continue;
2212 }
2213
2214 // get all activities for other case.
2215 $otherCaseActivities = array();
2216 CRM_Core_DAO::commonRetrieveAll('CRM_Case_DAO_CaseActivity', 'case_id', $otherCaseId, $otherCaseActivities);
2217
2218 //for duplicate cases do not process singleton activities.
2219 $otherActivityIds = $singletonActivityIds = array();
2220 foreach ($otherCaseActivities as $caseActivityId => $otherIds) {
2221 $otherActId = CRM_Utils_Array::value('activity_id', $otherIds);
2222 if (!$otherActId || in_array($otherActId, $otherActivityIds)) {
2223 continue;
2224 }
2225 $otherActivityIds[] = $otherActId;
2226 }
2227 if ($duplicateCases) {
2228 if ($openCaseType = array_search('Open Case', $activityTypes)) {
2229 $sql = "
2230SELECT id
2231 FROM civicrm_activity
2232 WHERE activity_type_id = $openCaseType
2233 AND id IN ( " . implode(',', array_values($otherActivityIds)) . ');';
2234 $dao = CRM_Core_DAO::executeQuery($sql);
2235 while ($dao->fetch()) {
2236 $singletonActivityIds[] = $dao->id;
2237 }
2238 $dao->free();
2239 }
2240 }
2241
2242 // migrate all activities and connect to main contact.
2243 $copiedActivityIds = $activityMappingIds = array();
2244 sort($otherActivityIds);
2245 foreach ($otherActivityIds as $otherActivityId) {
2246
2247 //for duplicate cases -
2248 //do not migrate singleton activities.
2249 if (!$otherActivityId || in_array($otherActivityId, $singletonActivityIds)) {
2250 continue;
2251 }
2252
2253 //migrate activity record.
2254 $otherActivity = new CRM_Activity_DAO_Activity();
2255 $otherActivity->id = $otherActivityId;
2256 if (!$otherActivity->find(TRUE)) {
2257 continue;
2258 }
2259
2260 $mainActVals = array();
2261 $mainActivity = new CRM_Activity_DAO_Activity();
2262 CRM_Core_DAO::storeValues($otherActivity, $mainActVals);
2263 $mainActivity->copyValues($mainActVals);
2264 $mainActivity->id = NULL;
2265 $mainActivity->activity_date_time = CRM_Utils_Date::isoToMysql($otherActivity->activity_date_time);
6a488035
TO
2266 $mainActivity->source_record_id = CRM_Utils_Array::value($mainActivity->source_record_id,
2267 $activityMappingIds
2268 );
2269
2270 $mainActivity->original_id = CRM_Utils_Array::value($mainActivity->original_id,
2271 $activityMappingIds
2272 );
2273
2274 $mainActivity->parent_id = CRM_Utils_Array::value($mainActivity->parent_id,
2275 $activityMappingIds
2276 );
2277 $mainActivity->save();
2278 $mainActivityId = $mainActivity->id;
2279 if (!$mainActivityId) {
2280 continue;
2281 }
2282
2283 $activityMappingIds[$otherActivityId] = $mainActivityId;
4322672b 2284 // insert log of all activities
6a488035
TO
2285 CRM_Activity_BAO_Activity::logActivityAction($mainActivity);
2286
2287 $otherActivity->free();
2288 $mainActivity->free();
2289 $copiedActivityIds[] = $otherActivityId;
2290
2291 //create case activity record.
2292 $mainCaseActivity = new CRM_Case_DAO_CaseActivity();
2293 $mainCaseActivity->case_id = $mainCaseId;
2294 $mainCaseActivity->activity_id = $mainActivityId;
2295 $mainCaseActivity->save();
2296 $mainCaseActivity->free();
2297
4322672b 2298 //migrate source activity.
2299 $otherSourceActivity = new CRM_Activity_DAO_ActivityContact();
2300 $otherSourceActivity->activity_id = $otherActivityId;
2301 $otherSourceActivity->record_type_id = $sourceID;
2302 $otherSourceActivity->find();
2303 while ($otherSourceActivity->fetch()) {
2304 $mainActivitySource = new CRM_Activity_DAO_ActivityContact();
2305 $mainActivitySource->record_type_id = $sourceID;
2306 $mainActivitySource->activity_id = $mainActivityId;
2307 $mainActivitySource->contact_id = $otherSourceActivity->contact_id;
2308 if ($mainActivitySource->contact_id == $otherContactId) {
2309 $mainActivitySource->contact_id = $mainContactId;
2310 }
2311 //avoid duplicate object.
2312 if (!$mainActivitySource->find(TRUE)) {
2313 $mainActivitySource->save();
2314 }
2315 $mainActivitySource->free();
2316 }
2317 $otherSourceActivity->free();
2318
6a488035 2319 //migrate target activities.
4e3d3cfc 2320 $otherTargetActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2321 $otherTargetActivity->activity_id = $otherActivityId;
9e74e3ce 2322 $otherTargetActivity->record_type_id = $targetID;
6a488035
TO
2323 $otherTargetActivity->find();
2324 while ($otherTargetActivity->fetch()) {
4e3d3cfc 2325 $mainActivityTarget = new CRM_Activity_DAO_ActivityContact();
9e74e3ce 2326 $mainActivityTarget->record_type_id = $targetID;
6a488035 2327 $mainActivityTarget->activity_id = $mainActivityId;
00bf7e59 2328 $mainActivityTarget->contact_id = $otherTargetActivity->contact_id;
2329 if ($mainActivityTarget->contact_id == $otherContactId) {
2330 $mainActivityTarget->contact_id = $mainContactId;
6a488035
TO
2331 }
2332 //avoid duplicate object.
2333 if (!$mainActivityTarget->find(TRUE)) {
2334 $mainActivityTarget->save();
2335 }
2336 $mainActivityTarget->free();
2337 }
2338 $otherTargetActivity->free();
2339
2340 //migrate assignee activities.
4e3d3cfc 2341 $otherAssigneeActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2342 $otherAssigneeActivity->activity_id = $otherActivityId;
9e74e3ce 2343 $otherAssigneeActivity->record_type_id = $assigneeID;
6a488035
TO
2344 $otherAssigneeActivity->find();
2345 while ($otherAssigneeActivity->fetch()) {
4e3d3cfc 2346 $mainAssigneeActivity = new CRM_Activity_DAO_ActivityContact();
6a488035 2347 $mainAssigneeActivity->activity_id = $mainActivityId;
9e74e3ce 2348 $mainAssigneeActivity->record_type_id = $assigneeID;
00bf7e59 2349 $mainAssigneeActivity->contact_id = $otherAssigneeActivity->contact_id;
2350 if ($mainAssigneeActivity->contact_id == $otherContactId) {
2351 $mainAssigneeActivity->contact_id = $mainContactId;
6a488035
TO
2352 }
2353 //avoid duplicate object.
2354 if (!$mainAssigneeActivity->find(TRUE)) {
2355 $mainAssigneeActivity->save();
2356 }
2357 $mainAssigneeActivity->free();
2358 }
2359 $otherAssigneeActivity->free();
8c31eef5
D
2360
2361 // copy custom fields and attachments
4322672b 2362 $aparams = array(
2363 'activityID' => $otherActivityId,
2364 'mainActivityId' => $mainActivityId,
2365 );
8c31eef5 2366 CRM_Activity_BAO_Activity::copyExtendedActivityData($aparams);
6a488035
TO
2367 }
2368
2369 //copy case relationship.
2370 if ($duplicateContacts) {
2371 //migrate relationship records.
2372 $otherRelationship = new CRM_Contact_DAO_Relationship();
2373 $otherRelationship->case_id = $otherCaseId;
2374 $otherRelationship->find();
2375 $otherRelationshipIds = array();
2376 while ($otherRelationship->fetch()) {
2377 $otherRelVals = array();
2378 $updateOtherRel = FALSE;
2379 CRM_Core_DAO::storeValues($otherRelationship, $otherRelVals);
2380
2381 $mainRelationship = new CRM_Contact_DAO_Relationship();
2382 $mainRelationship->copyValues($otherRelVals);
2383 $mainRelationship->id = NULL;
2384 $mainRelationship->case_id = $mainCaseId;
2385 if ($mainRelationship->contact_id_a == $otherContactId) {
2386 $updateOtherRel = TRUE;
2387 $mainRelationship->contact_id_a = $mainContactId;
2388 }
2389
2390 //case creator change only when we merge user contact.
2391 if ($mainRelationship->contact_id_b == $otherContactId) {
2392 //do not change creator for change client.
2393 if (!$changeClient) {
2394 $updateOtherRel = TRUE;
2395 $mainRelationship->contact_id_b = ($currentUserId) ? $currentUserId : $mainContactId;
2396 }
2397 }
2398 $mainRelationship->end_date = CRM_Utils_Date::isoToMysql($otherRelationship->end_date);
2399 $mainRelationship->start_date = CRM_Utils_Date::isoToMysql($otherRelationship->start_date);
2400
2401 //avoid duplicate object.
2402 if (!$mainRelationship->find(TRUE)) {
2403 $mainRelationship->save();
2404 }
2405 $mainRelationship->free();
2406
2407 //get the other relationship ids to update end date.
2408 if ($updateOtherRel) {
2409 $otherRelationshipIds[$otherRelationship->id] = $otherRelationship->id;
2410 }
2411 }
2412 $otherRelationship->free();
2413
2414 //update other relationships end dates
2415 if (!empty($otherRelationshipIds)) {
2416 $sql = 'UPDATE civicrm_relationship
2417 SET end_date = CURDATE()
2418 WHERE id IN ( ' . implode(',', $otherRelationshipIds) . ')';
2419 CRM_Core_DAO::executeQuery($sql);
2420 }
2421 }
2422
2423 //move other case to trash.
2424 $mergeCase = self::deleteCase($otherCaseId, $moveToTrash);
2425 if (!$mergeCase) {
2426 continue;
2427 }
2428
2429 $mergeActSubject = $mergeActSubjectDetails = $mergeActType = '';
2430 if ($changeClient) {
2431 $mainContactDisplayName = CRM_Contact_BAO_Contact::displayName($mainContactId);
2432 $otherContactDisplayName = CRM_Contact_BAO_Contact::displayName($otherContactId);
2433
2434 $mergeActType = array_search('Reassigned Case', $activityTypes);
2435 $mergeActSubject = ts("Case %1 reassigned client from %2 to %3. New Case ID is %4.",
2436 array(
e96f025a 2437 1 => $otherCaseId,
2438 2 => $otherContactDisplayName,
2439 3 => $mainContactDisplayName,
21dfd5f5 2440 4 => $mainCaseId,
6a488035
TO
2441 )
2442 );
2443 }
2444 elseif ($duplicateContacts) {
2445 $mergeActType = array_search('Merge Case', $activityTypes);
2446 $mergeActSubject = ts("Case %1 copied from contact id %2 to contact id %3 via merge. New Case ID is %4.",
2447 array(
e96f025a 2448 1 => $otherCaseId,
2449 2 => $otherContactId,
2450 3 => $mainContactId,
21dfd5f5 2451 4 => $mainCaseId,
6a488035
TO
2452 )
2453 );
2454 }
2455 else {
2456 $mergeActType = array_search('Merge Case', $activityTypes);
2457 $mergeActSubject = ts("Case %1 merged into case %2", array(1 => $otherCaseId, 2 => $mainCaseId));
2458 if (!empty($copiedActivityIds)) {
2459 $sql = '
2460SELECT id, subject, activity_date_time, activity_type_id
2461FROM civicrm_activity
2462WHERE id IN (' . implode(',', $copiedActivityIds) . ')';
2463 $dao = CRM_Core_DAO::executeQuery($sql);
2464 while ($dao->fetch()) {
2465 $mergeActSubjectDetails .= "{$dao->activity_date_time} :: {$activityTypes[$dao->activity_type_id]}";
2466 if ($dao->subject) {
2467 $mergeActSubjectDetails .= " :: {$dao->subject}";
2468 }
2469 $mergeActSubjectDetails .= "<br />";
2470 }
2471 }
2472 }
2473
8c677b07 2474 //Create merge activity record. Source for merge activity is the logged in user's contact ID ($currentUserId).
6a488035
TO
2475 $activityParams = array(
2476 'subject' => $mergeActSubject,
2477 'details' => $mergeActSubjectDetails,
2478 'status_id' => array_search('Completed', $activityStatuses),
2479 'activity_type_id' => $mergeActType,
8c677b07 2480 'source_contact_id' => $currentUserId,
6a488035
TO
2481 'activity_date_time' => date('YmdHis'),
2482 );
2483
2484 $mergeActivity = CRM_Activity_BAO_Activity::create($activityParams);
2485 $mergeActivityId = $mergeActivity->id;
2486 if (!$mergeActivityId) {
2487 continue;
2488 }
2489 $mergeActivity->free();
2490
2491 //connect merge activity to case.
2492 $mergeCaseAct = array(
2493 'case_id' => $mainCaseId,
2494 'activity_id' => $mergeActivityId,
2495 );
2496
2497 self::processCaseActivity($mergeCaseAct);
2498 }
02094cdb
JJ
2499
2500 CRM_Utils_Hook::post_case_merge($mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient);
2501
6a488035
TO
2502 return $mainCaseIds;
2503 }
2504
2505 /**
2506 * Validate contact permission for
2507 * edit/view on activity record and build links.
2508 *
64bd5a0e
TO
2509 * @param array $tplParams
2510 * Params to be sent to template for sending email.
2511 * @param array $activityParams
2512 * Info of the activity.
6a488035 2513 */
00be9182 2514 public static function buildPermissionLinks(&$tplParams, $activityParams) {
6a488035
TO
2515 $activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityParams['source_record_id'],
2516 'activity_type_id', 'id'
2517 );
2518
a7488080 2519 if (!empty($tplParams['isCaseActivity'])) {
6a488035
TO
2520 $tplParams['editActURL'] = CRM_Utils_System::url('civicrm/case/activity',
2521 "reset=1&cid={$activityParams['target_id']}&caseid={$activityParams['case_id']}&action=update&id={$activityParams['source_record_id']}", TRUE
2522 );
2523
2524 $tplParams['viewActURL'] = CRM_Utils_System::url('civicrm/case/activity/view',
2525 "reset=1&aid={$activityParams['source_record_id']}&cid={$activityParams['target_id']}&caseID={$activityParams['case_id']}", TRUE
2526 );
2527
2528 $tplParams['manageCaseURL'] = CRM_Utils_System::url('civicrm/contact/view/case',
2529 "reset=1&id={$activityParams['case_id']}&cid={$activityParams['target_id']}&action=view&context=home", TRUE
2530 );
2531 }
2532 else {
2533 $tplParams['editActURL'] = CRM_Utils_System::url('civicrm/contact/view/activity',
2534 "atype=$activityTypeId&action=update&reset=1&id={$activityParams['source_record_id']}&cid={$tplParams['contact']['contact_id']}&context=activity", TRUE
2535 );
2536
2537 $tplParams['viewActURL'] = CRM_Utils_System::url('civicrm/contact/view/activity',
2538 "atype=$activityTypeId&action=view&reset=1&id={$activityParams['source_record_id']}&cid={$tplParams['contact']['contact_id']}&context=activity", TRUE
2539 );
2540 }
2541 }
2542
2543 /**
2544 * Validate contact permission for
2545 * given operation on activity record.
2546 *
64bd5a0e
TO
2547 * @param int $activityId
2548 * Activity record id.
2549 * @param string $operation
2550 * User operation.
2551 * @param int $actTypeId
2552 * Activity type id.
2553 * @param int $contactId
2554 * Contact id/if not pass consider logged in.
2555 * @param bool $checkComponent
2556 * Do we need to check component enabled.
6a488035 2557 *
a130e045 2558 * @return bool
6a488035 2559 */
00be9182 2560 public static function checkPermission($activityId, $operation, $actTypeId = NULL, $contactId = NULL, $checkComponent = TRUE) {
6a488035
TO
2561 $allow = FALSE;
2562 if (!$actTypeId && $activityId) {
2563 $actTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id');
2564 }
2565
2566 if (!$activityId || !$operation || !$actTypeId) {
2567 return $allow;
2568 }
2569
2570 //do check for civicase component enabled.
077dbf5e
CW
2571 if ($checkComponent && !self::enabled()) {
2572 return $allow;
6a488035
TO
2573 }
2574
2575 //do check for cases.
2576 $caseActOperations = array(
2577 'File On Case',
2578 'Link Cases',
2579 'Move To Case',
2580 'Copy To Case',
2581 );
2582
2583 if (in_array($operation, $caseActOperations)) {
abd06efc
CW
2584 static $caseCount;
2585 if (!isset($caseCount)) {
eeb45e43
CW
2586 try {
2587 $caseCount = civicrm_api3('Case', 'getcount', array(
2588 'check_permissions' => TRUE,
2589 'status_id' => array('!=' => 'Closed'),
2590 'is_deleted' => 0,
2591 'end_date' => array('IS NULL' => 1),
2592 ));
2593 }
2594 catch (CiviCRM_API3_Exception $e) {
2595 // Lack of permissions will throw an exception
2596 $caseCount = 0;
2597 }
6a488035
TO
2598 }
2599 if ($operation == 'File On Case') {
abd06efc 2600 $allow = !empty($caseCount);
6a488035
TO
2601 }
2602 else {
abd06efc 2603 $allow = ($caseCount > 1);
6a488035
TO
2604 }
2605 }
2606
2607 $actionOperations = array('view', 'edit', 'delete');
2608 if (in_array($operation, $actionOperations)) {
2609
2610 //do cache when user has non/supper permission.
2611 static $allowOperations;
2612
2613 if (!is_array($allowOperations) ||
2614 !array_key_exists($operation, $allowOperations)
2615 ) {
2616
2617 if (!$contactId) {
2618 $session = CRM_Core_Session::singleton();
2619 $contactId = $session->get('userID');
2620 }
2621
2622 //check for permissions.
2623 $permissions = array(
2624 'view' => array(
2625 'access my cases and activities',
2626 'access all cases and activities',
2627 ),
2628 'edit' => array(
2629 'access my cases and activities',
2630 'access all cases and activities',
2631 ),
2632 'delete' => array('delete activities'),
2633 );
2634
2635 //check for core permission.
2636 $hasPermissions = array();
2637 $checkPermissions = CRM_Utils_Array::value($operation, $permissions);
2638 if (is_array($checkPermissions)) {
2639 foreach ($checkPermissions as $per) {
2640 if (CRM_Core_Permission::check($per)) {
2641 $hasPermissions[$operation][] = $per;
2642 }
2643 }
2644 }
2645
2646 //has permissions.
2647 if (!empty($hasPermissions)) {
2648 //need to check activity object specific.
2649 if (in_array($operation, array(
e96f025a 2650 'view',
21dfd5f5 2651 'edit',
e96f025a 2652 ))
2653 ) {
6a488035
TO
2654 //do we have supper permission.
2655 if (in_array('access all cases and activities', $hasPermissions[$operation])) {
2656 $allowOperations[$operation] = $allow = TRUE;
2657 }
2658 else {
2659 //user has only access to my cases and activity.
2660 //here object specific permmions come in picture.
2661
2662 //edit - contact must be source or assignee
2663 //view - contact must be source/assignee/target
2664 $isTarget = $isAssignee = $isSource = FALSE;
44f817d4 2665 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
4322672b 2666 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
2667 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
2668 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
6a488035 2669
4e3d3cfc 2670 $target = new CRM_Activity_DAO_ActivityContact();
9e74e3ce 2671 $target->record_type_id = $targetID;
6a488035 2672 $target->activity_id = $activityId;
00bf7e59 2673 $target->contact_id = $contactId;
6a488035
TO
2674 if ($target->find(TRUE)) {
2675 $isTarget = TRUE;
2676 }
2677
4e3d3cfc 2678 $assignee = new CRM_Activity_DAO_ActivityContact();
6a488035 2679 $assignee->activity_id = $activityId;
9e74e3ce 2680 $assignee->record_type_id = $assigneeID;
00bf7e59 2681 $assignee->contact_id = $contactId;
6a488035
TO
2682 if ($assignee->find(TRUE)) {
2683 $isAssignee = TRUE;
2684 }
2685
4322672b 2686 $source = new CRM_Activity_DAO_ActivityContact();
2687 $source->activity_id = $activityId;
2688 $source->record_type_id = $sourceID;
2689 $source->contact_id = $contactId;
2690 if ($source->find(TRUE)) {
6a488035
TO
2691 $isSource = TRUE;
2692 }
2693
2694 if ($operation == 'edit') {
2695 if ($isAssignee || $isSource) {
2696 $allow = TRUE;
2697 }
2698 }
2699 if ($operation == 'view') {
2700 if ($isTarget || $isAssignee || $isSource) {
2701 $allow = TRUE;
2702 }
2703 }
2704 }
2705 }
2706 elseif (is_array($hasPermissions[$operation])) {
2707 $allowOperations[$operation] = $allow = TRUE;
2708 }
2709 }
2710 else {
2711 //contact do not have permission.
2712 $allowOperations[$operation] = FALSE;
2713 }
2714 }
2715 else {
2716 //use cache.
2717 //here contact might have supper/non permission.
2718 $allow = $allowOperations[$operation];
2719 }
2720 }
2721
2722 //do further only when operation is granted.
2723 if ($allow) {
2724 $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, FALSE, 'name');
2725
2726 //get the activity type name.
2727 $actTypeName = CRM_Utils_Array::value($actTypeId, $activityTypes);
2728
2729 //do not allow multiple copy / edit action.
e96f025a 2730 $singletonNames = array(
2731 'Open Case',
2732 'Reassigned Case',
2733 'Merge Case',
2734 'Link Cases',
2735 'Assign Case Role',
2736 'Email',
21dfd5f5 2737 'Inbound Email',
e96f025a 2738 );
6a488035
TO
2739
2740 //do not allow to delete these activities, CRM-4543
2741 $doNotDeleteNames = array('Open Case', 'Change Case Type', 'Change Case Status', 'Change Case Start Date');
2742
2743 //allow edit operation.
2744 $allowEditNames = array('Open Case');
2745
2746 // do not allow File on Case
e96f025a 2747 $doNotFileNames = array(
2748 'Open Case',
2749 'Change Case Type',
2750 'Change Case Status',
2751 'Change Case Start Date',
2752 'Reassigned Case',
2753 'Merge Case',
2754 'Link Cases',
21dfd5f5 2755 'Assign Case Role',
e96f025a 2756 );
6a488035
TO
2757
2758 if (in_array($actTypeName, $singletonNames)) {
2759 $allow = FALSE;
2760 if ($operation == 'File On Case') {
2761 $allow = (in_array($actTypeName, $doNotFileNames)) ? FALSE : TRUE;
2762 }
2763 if (in_array($operation, $actionOperations)) {
2764 $allow = TRUE;
2765 if ($operation == 'edit') {
2766 $allow = (in_array($actTypeName, $allowEditNames)) ? TRUE : FALSE;
2767 }
2768 elseif ($operation == 'delete') {
2769 $allow = (in_array($actTypeName, $doNotDeleteNames)) ? FALSE : TRUE;
2770 }
2771 }
2772 }
2773 if ($allow && ($operation == 'delete') &&
2774 in_array($actTypeName, $doNotDeleteNames)
2775 ) {
2776 $allow = FALSE;
2777 }
2778
2779 if ($allow && ($operation == 'File On Case') &&
2780 in_array($actTypeName, $doNotFileNames)
2781 ) {
2782 $allow = FALSE;
2783 }
2784
2785 //check settings file for masking actions
2786 //on the basis the activity types
2787 //hide Edit link if activity type is NOT editable
2788 //(special case activities).CRM-5871
2789 if ($allow && in_array($operation, $actionOperations)) {
2790 static $actionFilter = array();
2791 if (!array_key_exists($operation, $actionFilter)) {
2792 $xmlProcessor = new CRM_Case_XMLProcessor_Process();
2793 $actionFilter[$operation] = $xmlProcessor->get('Settings', 'ActivityTypes', FALSE, $operation);
2794 }
2795 if (array_key_exists($operation, $actionFilter[$operation]) &&
2796 in_array($actTypeId, $actionFilter[$operation][$operation])
2797 ) {
2798 $allow = FALSE;
2799 }
2800 }
2801 }
2802
2803 return $allow;
2804 }
2805
2806 /**
100fef9d 2807 * Since we drop 'access CiviCase', allow access
6a488035
TO
2808 * if user has 'access my cases and activities'
2809 * or 'access all cases and activities'
2810 */
00be9182 2811 public static function accessCiviCase() {
077dbf5e 2812 if (!self::enabled()) {
6a488035
TO
2813 return FALSE;
2814 }
2815
2816 if (CRM_Core_Permission::check('access my cases and activities') ||
2817 CRM_Core_Permission::check('access all cases and activities')
2818 ) {
2819 return TRUE;
2820 }
2821
2822 return FALSE;
2823 }
2824
2825 /**
d2e5d2ce 2826 * Verify user has permission to access a case.
077dbf5e
CW
2827 *
2828 * @param int $caseId
64bd5a0e
TO
2829 * @param bool $denyClosed
2830 * Set TRUE if one wants closed cases to be treated as inaccessible.
077dbf5e
CW
2831 *
2832 * @return bool
2833 */
00be9182 2834 public static function accessCase($caseId, $denyClosed = TRUE) {
077dbf5e
CW
2835 if (!$caseId || !self::enabled()) {
2836 return FALSE;
2837 }
2838
3924e596
CW
2839 $params = array('id' => $caseId, 'check_permissions' => TRUE);
2840 if ($denyClosed && !CRM_Core_Permission::check('access all cases and activities')) {
2841 $params['status_id'] = array('!=' => 'Closed');
546abeb1 2842 }
eeb45e43
CW
2843 try {
2844 return (bool) civicrm_api3('Case', 'getcount', $params);
2845 }
2846 catch (CiviCRM_API3_Exception $e) {
2847 // Lack of permissions will throw an exception
2848 return FALSE;
2849 }
077dbf5e
CW
2850 }
2851
6a488035 2852 /**
d2e5d2ce 2853 * Check whether activity is a case Activity.
6a488035 2854 *
64bd5a0e
TO
2855 * @param int $activityID
2856 * Activity id.
6a488035 2857 *
a130e045 2858 * @return bool
6a488035 2859 */
00be9182 2860 public static function isCaseActivity($activityID) {
6a488035
TO
2861 $isCaseActivity = FALSE;
2862 if ($activityID) {
2863 $params = array(1 => array($activityID, 'Integer'));
2864 $query = "SELECT id FROM civicrm_case_activity WHERE activity_id = %1";
2865 if (CRM_Core_DAO::singleValueQuery($query, $params)) {
2866 $isCaseActivity = TRUE;
2867 }
2868 }
2869
2870 return $isCaseActivity;
2871 }
2872
2873 /**
d2e5d2ce 2874 * Get all the case type ids currently in use.
6a488035 2875 *
a6c01b45 2876 * @return array
6a488035 2877 */
00be9182 2878 public static function getUsedCaseType() {
6a488035
TO
2879 static $caseTypeIds;
2880
2881 if (!is_array($caseTypeIds)) {
2882 $query = "SELECT DISTINCT( civicrm_case.case_type_id ) FROM civicrm_case";
2883
2884 $dao = CRM_Core_DAO::executeQuery($query);
2885 $caseTypeIds = array();
2886 while ($dao->fetch()) {
2887 $typeId = explode(CRM_Core_DAO::VALUE_SEPARATOR,
2888 $dao->case_type_id
2889 );
2890 $caseTypeIds[] = $typeId[1];
2891 }
2892 }
2893
2894 return $caseTypeIds;
2895 }
2896
2897 /**
d2e5d2ce 2898 * Get all the case status ids currently in use.
6a488035 2899 *
a6c01b45 2900 * @return array
6a488035 2901 */
00be9182 2902 public static function getUsedCaseStatuses() {
6a488035
TO
2903 static $caseStatusIds;
2904
2905 if (!is_array($caseStatusIds)) {
2906 $query = "SELECT DISTINCT( civicrm_case.status_id ) FROM civicrm_case";
2907
2908 $dao = CRM_Core_DAO::executeQuery($query);
2909 $caseStatusIds = array();
2910 while ($dao->fetch()) {
2911 $caseStatusIds[] = $dao->status_id;
2912 }
2913 }
2914
2915 return $caseStatusIds;
2916 }
2917
2918 /**
d2e5d2ce 2919 * Get all the encounter medium ids currently in use.
72b3a70c 2920 *
6a488035
TO
2921 * @return array
2922 */
00be9182 2923 public static function getUsedEncounterMediums() {
6a488035
TO
2924 static $mediumIds;
2925
2926 if (!is_array($mediumIds)) {
2927 $query = "SELECT DISTINCT( civicrm_activity.medium_id ) FROM civicrm_activity";
2928
2929 $dao = CRM_Core_DAO::executeQuery($query);
2930 $mediumIds = array();
2931 while ($dao->fetch()) {
2932 $mediumIds[] = $dao->medium_id;
2933 }
2934 }
2935
2936 return $mediumIds;
2937 }
2938
2939 /**
100fef9d 2940 * Check case configuration.
6a488035 2941 *
100fef9d 2942 * @param int $contactId
77b97be7 2943 *
a6c01b45 2944 * @return array
6a488035 2945 */
00be9182 2946 public static function isCaseConfigured($contactId = NULL) {
6a488035
TO
2947 $configured = array_fill_keys(array('configured', 'allowToAddNewCase', 'redirectToCaseAdmin'), FALSE);
2948
2949 //lets check for case configured.
2950 $allCasesCount = CRM_Case_BAO_Case::caseCount(NULL, FALSE);
2951 $configured['configured'] = ($allCasesCount) ? TRUE : FALSE;
2952 if (!$configured['configured']) {
2953 //do check for case type and case status.
0372ffa2 2954 $caseTypes = CRM_Case_PseudoConstant::caseType('title', FALSE);
6a488035
TO
2955 if (!empty($caseTypes)) {
2956 $configured['configured'] = TRUE;
2957 if (!$configured['configured']) {
2958 $caseStatuses = CRM_Case_PseudoConstant::caseStatus('label', FALSE);
2959 if (!empty($caseStatuses)) {
2960 $configured['configured'] = TRUE;
2961 }
2962 }
2963 }
2964 }
2965 if ($configured['configured']) {
2966 //do check for active case type and case status.
2967 $caseTypes = CRM_Case_PseudoConstant::caseType();
2968 if (!empty($caseTypes)) {
2969 $caseStatuses = CRM_Case_PseudoConstant::caseStatus();
2970 if (!empty($caseStatuses)) {
2971 $configured['allowToAddNewCase'] = TRUE;
2972 }
2973 }
2974
2975 //do we need to redirect user to case admin.
2976 if (!$configured['allowToAddNewCase'] && $contactId) {
2977 //check for current contact case count.
2978 $currentContatCasesCount = CRM_Case_BAO_Case::caseCount($contactId);
2979 //redirect user to case admin page.
2980 if (!$currentContatCasesCount) {
2981 $configured['redirectToCaseAdmin'] = TRUE;
2982 }
2983 }
2984 }
2985
2986 return $configured;
2987 }
2988
d6f468d3 2989 /**
d2e5d2ce 2990 * Used during case component enablement and during ugprade.
72b3a70c
CW
2991 *
2992 * @return bool
6a488035 2993 */
00be9182 2994 public static function createCaseViews() {
6a4257d4 2995 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
a19fc402
TO
2996 $dao = new CRM_Core_DAO();
2997
6a488035 2998 $sql = self::createCaseViewsQuery('upcoming');
6a488035
TO
2999 $dao->query($sql);
3000 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
3001 return FALSE;
3002 }
3003
3004 // Above error doesn't get caught?
3005 $doublecheck = $dao->singleValueQuery("SELECT count(id) FROM civicrm_view_case_activity_upcoming");
3006 if (is_null($doublecheck)) {
3007 return FALSE;
3008 }
3009
3010 $sql = self::createCaseViewsQuery('recent');
6a488035
TO
3011 $dao->query($sql);
3012 if (PEAR::getStaticProperty('DB_DataObject', 'lastError')) {
6a488035
TO
3013 return FALSE;
3014 }
3015
3016 // Above error doesn't get caught?
3017 $doublecheck = $dao->singleValueQuery("SELECT count(id) FROM civicrm_view_case_activity_recent");
3018 if (is_null($doublecheck)) {
3019 return FALSE;
3020 }
3021
3022 return TRUE;
3023 }
3024
d6f468d3 3025 /**
100fef9d 3026 * Helper function, also used by the upgrade in case of error
72b3a70c 3027 *
cde2037d
EM
3028 * @param string $section
3029 *
72b3a70c 3030 * @return string
6a488035 3031 */
00be9182 3032 public static function createCaseViewsQuery($section = 'upcoming') {
6a488035 3033 $sql = "";
d66c61b6 3034 $scheduled_id = CRM_Core_Pseudoconstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Scheduled');
6a488035
TO
3035 switch ($section) {
3036 case 'upcoming':
3037 $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_upcoming`
3038 AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
3039 FROM civicrm_case_activity ca
3040 INNER JOIN civicrm_activity a ON ca.activity_id=a.id
3cf1fae9 3041 WHERE a.activity_date_time =
2b95a25b 3042(SELECT b.activity_date_time FROM civicrm_case_activity bca
3043 INNER JOIN civicrm_activity b ON bca.activity_id=b.id
3044 WHERE b.activity_date_time <= DATE_ADD( NOW(), INTERVAL 14 DAY )
3045 AND b.is_current_revision = 1 AND b.is_deleted=0 AND b.status_id = $scheduled_id
3046 AND bca.case_id = ca.case_id ORDER BY b.activity_date_time ASC LIMIT 1)";
6a488035
TO
3047 break;
3048
3049 case 'recent':
3050 $sql = "CREATE OR REPLACE VIEW `civicrm_view_case_activity_recent`
3051 AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
3052 FROM civicrm_case_activity ca
3053 INNER JOIN civicrm_activity a ON ca.activity_id=a.id
3cf1fae9 3054 WHERE a.activity_date_time =
2b95a25b 3055(SELECT b.activity_date_time FROM civicrm_case_activity bca
3056 INNER JOIN civicrm_activity b ON bca.activity_id=b.id
3057 WHERE b.activity_date_time >= DATE_SUB( NOW(), INTERVAL 14 DAY )
3058 AND b.is_current_revision = 1 AND b.is_deleted=0 AND b.status_id <> $scheduled_id
3059 AND bca.case_id = ca.case_id ORDER BY b.activity_date_time DESC LIMIT 1)";
6a488035
TO
3060 break;
3061 }
6a488035 3062 return $sql;
e96f025a 3063 }
3064
3065 /**
100fef9d 3066 * Add/copy relationships, when new client is added for a case
e96f025a 3067 *
64bd5a0e
TO
3068 * @param int $caseId
3069 * Case id.
3070 * @param int $contactId
3071 * Contact id / new client id.
e96f025a 3072 */
00be9182 3073 public static function addCaseRelationships($caseId, $contactId) {
44466d80
KJ
3074 // get the case role / relationships for the case
3075 $caseRelationships = new CRM_Contact_DAO_Relationship();
3076 $caseRelationships->case_id = $caseId;
3077 $caseRelationships->find();
3078 $relationshipTypes = array();
3079
3080 // make sure we don't add duplicate relationships of same relationship type.
3081 while ($caseRelationships->fetch() && !in_array($caseRelationships->relationship_type_id, $relationshipTypes)) {
3082 $values = array();
3083 CRM_Core_DAO::storeValues($caseRelationships, $values);
3084
3085 // add relationship for new client.
3086 $newRelationship = new CRM_Contact_DAO_Relationship();
3087 $newRelationship->copyValues($values);
3088 $newRelationship->id = NULL;
3089 $newRelationship->case_id = $caseId;
3090 $newRelationship->contact_id_a = $contactId;
3091 $newRelationship->end_date = CRM_Utils_Date::isoToMysql($caseRelationships->end_date);
3092 $newRelationship->start_date = CRM_Utils_Date::isoToMysql($caseRelationships->start_date);
3093
3094 // another check to avoid duplicate relationship, in cases where client is removed and re-added again.
3095 if (!$newRelationship->find(TRUE)) {
3096 $newRelationship->save();
3097 }
3098 $newRelationship->free();
3099
3100 // store relationship type of newly created relationship
3101 $relationshipTypes[] = $caseRelationships->relationship_type_id;
3102 }
6a488035 3103 }
14a679f1
KJ
3104
3105 /**
d2e5d2ce 3106 * Get the list of clients for a case.
14a679f1
KJ
3107 *
3108 * @param int $caseId
3109 *
a6c01b45
CW
3110 * @return array
3111 * associated array with client ids
14a679f1 3112 */
00be9182 3113 public static function getCaseClients($caseId) {
14a679f1
KJ
3114 $clients = array();
3115 $caseContact = new CRM_Case_DAO_CaseContact();
3116 $caseContact->case_id = $caseId;
78762324 3117 $caseContact->orderBy('id');
14a679f1
KJ
3118 $caseContact->find();
3119
e96f025a 3120 while ($caseContact->fetch()) {
14a679f1
KJ
3121 $clients[] = $caseContact->contact_id;
3122 }
3123
3124 return $clients;
3125 }
16c0ec8d 3126
3b1c37fe
CW
3127 /**
3128 * @param int $caseId
3129 * @param string $direction
3130 * @param int $cid
3131 * @param int $relTypeId
3132 * @throws \CRM_Core_Exception
3133 * @throws \CiviCRM_API3_Exception
3134 */
3135 public static function endCaseRole($caseId, $direction, $cid, $relTypeId) {
3136 // Validate inputs
3137 if ($direction !== 'a' && $direction !== 'b') {
3138 throw new CRM_Core_Exception('Invalid relationship direction');
3139 }
3140
3141 // This case might have multiple clients, so we lookup by relationship instead of by id to get them all
3142 $sql = "SELECT id FROM civicrm_relationship WHERE case_id = %1 AND contact_id_{$direction} = %2 AND relationship_type_id = %3";
3143 $dao = CRM_Core_DAO::executeQuery($sql, array(
3144 1 => array($caseId, 'Positive'),
3145 2 => array($cid, 'Positive'),
3146 3 => array($relTypeId, 'Positive'),
3147 ));
3148 while ($dao->fetch()) {
3149 civicrm_api3('relationship', 'create', array(
3150 'id' => $dao->id,
3151 'is_active' => 0,
3152 'end_date' => 'now',
3153 ));
3154 }
3155 }
3156
16c0ec8d
CW
3157 /**
3158 * Get options for a given case field.
3159 * @see CRM_Core_DAO::buildOptions
3160 *
64bd5a0e
TO
3161 * @param string $fieldName
3162 * @param string $context
408b79bf 3163 * @see CRM_Core_DAO::buildOptionsContext
64bd5a0e 3164 * @param array $props
72b3a70c 3165 * Whatever is known about this dao object.
77b97be7 3166 *
a130e045 3167 * @return array|bool
16c0ec8d
CW
3168 */
3169 public static function buildOptions($fieldName, $context = NULL, $props = array()) {
3170 $className = __CLASS__;
3171 $params = array();
3172 switch ($fieldName) {
3173 // This field is not part of this object but the api supports it
3174 case 'medium_id':
3175 $className = 'CRM_Activity_BAO_Activity';
3176 break;
31c28ed5
CW
3177
3178 // Filter status id by case type id
3179 case 'status_id':
3180 if (!empty($props['case_type_id'])) {
3181 $idField = is_numeric($props['case_type_id']) ? 'id' : 'name';
3182 $caseType = civicrm_api3('CaseType', 'getsingle', array($idField => $props['case_type_id'], 'return' => 'definition'));
3183 if (!empty($caseType['definition']['statuses'])) {
3184 $params['condition'] = 'v.name IN ("' . implode('","', $caseType['definition']['statuses']) . '")';
3185 }
3186 }
3187 break;
16c0ec8d
CW
3188 }
3189 return CRM_Core_PseudoConstant::get($className, $fieldName, $params, $context);
3190 }
96025800 3191
174a1918
CW
3192 /**
3193 * @inheritDoc
3194 */
20e41014 3195 public function addSelectWhereClause() {
0b80f0b4
CW
3196 // We always return an array with these keys, even if they are empty,
3197 // because this tells the query builder that we have considered these fields for acls
ff9340a4
CW
3198 $clauses = array(
3199 'id' => array(),
3200 // Only case admins can view deleted cases
0b80f0b4 3201 'is_deleted' => CRM_Core_Permission::check('administer CiviCase') ? array() : array("= 0"),
ff9340a4 3202 );
174a1918 3203 // Ensure the user has permission to view the case client
d1d3c04a 3204 $contactClause = CRM_Utils_SQL::mergeSubquery('Contact');
ff9340a4
CW
3205 if ($contactClause) {
3206 $contactClause = implode(' AND contact_id ', $contactClause);
3207 $clauses['id'][] = "IN (SELECT case_id FROM civicrm_case_contact WHERE contact_id $contactClause)";
174a1918 3208 }
0b80f0b4 3209 // The api gatekeeper ensures the user has at least "access my cases and activities"
174a1918
CW
3210 // so if they do not have permission to see all cases we'll assume they can only access their own
3211 if (!CRM_Core_Permission::check('access all cases and activities')) {
3212 $user = (int) CRM_Core_Session::getLoggedInContactID();
ff9340a4 3213 $clauses['id'][] = "IN (
174a1918 3214 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 3215 (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
3216 )
3217 )";
3218 }
2b240c0c 3219 CRM_Utils_Hook::selectWhereClause($this, $clauses);
ff9340a4 3220 return $clauses;
174a1918
CW
3221 }
3222
8aeeea00 3223 /**
3cf1fae9 3224 * CRM-20308: Method to get the contact id to use as from contact for email copy
8aeeea00
EH
3225 * 1. Activity Added by Contact's email address
3226 * 2. System Default From Address
3227 * 3. Default Organization Contact email address
3228 * 4. Logged in user
3229 *
3cf1fae9 3230 * @param int $activityID
3231 *
8aeeea00
EH
3232 * @return mixed $emailFromContactId
3233 * @see https://issues.civicrm.org/jira/browse/CRM-20308
3234 */
3cf1fae9 3235 public static function getReceiptFrom($activityID) {
3236 $name = $address = NULL;
3237
4c981f37
MW
3238 if (!empty($activityID) && (Civi::settings()->get('allow_mail_from_logged_in_contact'))) {
3239 // This breaks SPF/DMARC if email is sent from an email address that the server is not authorised to send from.
3240 // so we can disable this behaviour with the "allow_mail_from_logged_in_contact" setting.
3cf1fae9 3241 // There is always a 'Added by' contact for a activity,
3242 // so we can safely use ActivityContact.Getvalue API
3243 $sourceContactId = civicrm_api3('ActivityContact', 'getvalue', array(
3244 'activity_id' => $activityID,
3245 'record_type_id' => 'Activity Source',
3246 'return' => 'contact_id',
3247 ));
3248 list($name, $address) = CRM_Contact_BAO_Contact_Location::getEmailDetails($sourceContactId);
8aeeea00 3249 }
3cf1fae9 3250
3251 // If 'From' email address not found for Source Activity Contact then
3252 // fetch the email from domain or logged in user.
3253 if (empty($address)) {
3254 list($name, $address) = CRM_Core_BAO_Domain::getDefaultReceiptFrom();
8aeeea00 3255 }
3cf1fae9 3256
8aeeea00
EH
3257 return "$name <$address>";
3258 }
3259
6a488035 3260}