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