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