Merge pull request #14494 from colemanw/customPc
[civicrm-core.git] / CRM / Case / XMLProcessor / Process.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
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
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
6a488035
TO
32 */
33class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor {
ad8d1ce3
RO
34 protected $defaultAssigneeOptionsValues = [];
35
4c6ce474 36 /**
67d19299 37 * Run.
38 *
39 * @param string $caseType
c490a46a 40 * @param array $params
4c6ce474 41 *
cde2037d 42 * @return bool
4c6ce474
EM
43 * @throws Exception
44 */
00be9182 45 public function run($caseType, &$params) {
6a488035
TO
46 $xml = $this->retrieve($caseType);
47
48 if ($xml === FALSE) {
6b02fdc3 49 $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
6a488035 50 CRM_Core_Error::fatal(ts("Configuration file could not be retrieved for case type = '%1' %2.",
be2fb01f 51 [1 => $caseType, 2 => $docLink]
353ffa53 52 ));
6a488035
TO
53 return FALSE;
54 }
55
56 $xmlProcessorProcess = new CRM_Case_XMLProcessor_Process();
57 $this->_isMultiClient = $xmlProcessorProcess->getAllowMultipleCaseClients();
58
59 $this->process($xml, $params);
60 }
61
4c6ce474
EM
62 /**
63 * @param $caseType
64 * @param $fieldSet
65 * @param bool $isLabel
66 * @param bool $maskAction
67 *
68 * @return array|bool|mixed
69 * @throws Exception
70 */
00be9182 71 public function get($caseType, $fieldSet, $isLabel = FALSE, $maskAction = FALSE) {
6a488035
TO
72 $xml = $this->retrieve($caseType);
73 if ($xml === FALSE) {
6b02fdc3 74 $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
6a488035 75 CRM_Core_Error::fatal(ts("Unable to load configuration file for the referenced case type: '%1' %2.",
be2fb01f 76 [1 => $caseType, 2 => $docLink]
353ffa53 77 ));
6a488035
TO
78 return FALSE;
79 }
80
81 switch ($fieldSet) {
82 case 'CaseRoles':
83 return $this->caseRoles($xml->CaseRoles);
84
85 case 'ActivitySets':
86 return $this->activitySets($xml->ActivitySets);
87
88 case 'ActivityTypes':
89 return $this->activityTypes($xml->ActivityTypes, FALSE, $isLabel, $maskAction);
90 }
91 }
92
4c6ce474
EM
93 /**
94 * @param $xml
c490a46a 95 * @param array $params
4c6ce474
EM
96 *
97 * @throws Exception
98 */
00be9182 99 public function process($xml, &$params) {
6a488035 100 $standardTimeline = CRM_Utils_Array::value('standardTimeline', $params);
353ffa53 101 $activitySetName = CRM_Utils_Array::value('activitySetName', $params);
6a488035
TO
102
103 if ('Open Case' == CRM_Utils_Array::value('activityTypeName', $params)) {
104 // create relationships for the ones that are required
105 foreach ($xml->CaseRoles as $caseRoleXML) {
106 foreach ($caseRoleXML->RelationshipType as $relationshipTypeXML) {
107 if ((int ) $relationshipTypeXML->creator == 1) {
108 if (!$this->createRelationships((string ) $relationshipTypeXML->name,
353ffa53
TO
109 $params
110 )
111 ) {
6a488035
TO
112 CRM_Core_Error::fatal();
113 return FALSE;
114 }
115 }
116 }
117 }
118 }
119
120 if ('Change Case Start Date' == CRM_Utils_Array::value('activityTypeName', $params)) {
121 // delete all existing activities which are non-empty
122 $this->deleteEmptyActivity($params);
123 }
124
125 foreach ($xml->ActivitySets as $activitySetsXML) {
126 foreach ($activitySetsXML->ActivitySet as $activitySetXML) {
127 if ($standardTimeline) {
128 if ((boolean ) $activitySetXML->timeline) {
129 return $this->processStandardTimeline($activitySetXML,
130 $params
131 );
132 }
133 }
134 elseif ($activitySetName) {
135 $name = (string ) $activitySetXML->name;
136 if ($name == $activitySetName) {
137 return $this->processActivitySet($activitySetXML,
138 $params
139 );
140 }
141 }
142 }
143 }
144 }
145
4c6ce474
EM
146 /**
147 * @param $activitySetXML
c490a46a 148 * @param array $params
4c6ce474 149 */
00be9182 150 public function processStandardTimeline($activitySetXML, &$params) {
448e7600 151 if ('Change Case Type' == CRM_Utils_Array::value('activityTypeName', $params)
353ffa53
TO
152 && CRM_Utils_Array::value('resetTimeline', $params, TRUE)
153 ) {
6a488035
TO
154 // delete all existing activities which are non-empty
155 $this->deleteEmptyActivity($params);
156 }
157
158 foreach ($activitySetXML->ActivityTypes as $activityTypesXML) {
159 foreach ($activityTypesXML as $activityTypeXML) {
160 $this->createActivity($activityTypeXML, $params);
161 }
162 }
163 }
164
4c6ce474
EM
165 /**
166 * @param $activitySetXML
c490a46a 167 * @param array $params
4c6ce474 168 */
00be9182 169 public function processActivitySet($activitySetXML, &$params) {
6a488035
TO
170 foreach ($activitySetXML->ActivityTypes as $activityTypesXML) {
171 foreach ($activityTypesXML as $activityTypeXML) {
172 $this->createActivity($activityTypeXML, $params);
173 }
174 }
175 }
176
4c6ce474
EM
177 /**
178 * @param $caseRolesXML
179 * @param bool $isCaseManager
180 *
181 * @return array|mixed
182 */
00be9182 183 public function &caseRoles($caseRolesXML, $isCaseManager = FALSE) {
d0a94888
AF
184 // Look up relationship types according to the XML convention (described
185 // from perspective of non-client) but return the labels according to the UI
186 // convention (described from perspective of client)
187 $relationshipTypes = &$this->allRelationshipTypes(TRUE);
188 $relationshipTypesToReturn = &$this->allRelationshipTypes(FALSE);
6a488035 189
be2fb01f 190 $result = [];
6a488035
TO
191 foreach ($caseRolesXML as $caseRoleXML) {
192 foreach ($caseRoleXML->RelationshipType as $relationshipTypeXML) {
193 $relationshipTypeName = (string ) $relationshipTypeXML->name;
194 $relationshipTypeID = array_search($relationshipTypeName,
195 $relationshipTypes
196 );
197 if ($relationshipTypeID === FALSE) {
198 continue;
199 }
200
201 if (!$isCaseManager) {
d0a94888 202 $result[$relationshipTypeID] = $relationshipTypesToReturn[$relationshipTypeID];
6a488035 203 }
913b5673 204 elseif ($relationshipTypeXML->manager == 1) {
6a488035
TO
205 return $relationshipTypeID;
206 }
207 }
208 }
209 return $result;
210 }
211
4c6ce474 212 /**
100fef9d 213 * @param string $relationshipTypeName
c490a46a 214 * @param array $params
4c6ce474
EM
215 *
216 * @return bool
217 * @throws Exception
218 */
00be9182 219 public function createRelationships($relationshipTypeName, &$params) {
d0a94888
AF
220 // The relationshipTypeName is coming from XML, so the argument should be
221 // `TRUE`
222 $relationshipTypes = &$this->allRelationshipTypes(TRUE);
41cf58d3
AF
223 // get the relationship
224 $relationshipType = array_search($relationshipTypeName, $relationshipTypes);
6a488035 225
41cf58d3 226 if ($relationshipType === FALSE) {
6b02fdc3 227 $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
6a488035 228 CRM_Core_Error::fatal(ts('Relationship type %1, found in case configuration file, is not present in the database %2',
be2fb01f 229 [1 => $relationshipTypeName, 2 => $docLink]
353ffa53 230 ));
6a488035
TO
231 return FALSE;
232 }
233
234 $client = $params['clientID'];
235 if (!is_array($client)) {
be2fb01f 236 $client = [$client];
6a488035
TO
237 }
238
239 foreach ($client as $key => $clientId) {
be2fb01f 240 $relationshipParams = [
41cf58d3 241 'relationship_type_id' => substr($relationshipType, 0, -4),
6a488035
TO
242 'is_active' => 1,
243 'case_id' => $params['caseID'],
244 'start_date' => date("Ymd"),
3717c347 245 'end_date' => CRM_Utils_Array::value('relationship_end_date', $params),
be2fb01f 246 ];
6a488035 247
41cf58d3
AF
248 if (substr($relationshipType, -4) == '_b_a') {
249 $relationshipParams['contact_id_b'] = $clientId;
250 $relationshipParams['contact_id_a'] = $params['creatorID'];
251 }
252 if (substr($relationshipType, -4) == '_a_b') {
253 $relationshipParams['contact_id_a'] = $clientId;
254 $relationshipParams['contact_id_b'] = $params['creatorID'];
255 }
256
6a488035
TO
257 if (!$this->createRelationship($relationshipParams)) {
258 CRM_Core_Error::fatal();
259 return FALSE;
260 }
261 }
262 return TRUE;
263 }
264
4c6ce474 265 /**
c490a46a 266 * @param array $params
4c6ce474
EM
267 *
268 * @return bool
269 */
00be9182 270 public function createRelationship(&$params) {
6a488035
TO
271 $dao = new CRM_Contact_DAO_Relationship();
272 $dao->copyValues($params);
273 // only create a relationship if it does not exist
274 if (!$dao->find(TRUE)) {
275 $dao->save();
276 }
277 return TRUE;
278 }
279
4c6ce474
EM
280 /**
281 * @param $activityTypesXML
282 * @param bool $maxInst
283 * @param bool $isLabel
284 * @param bool $maskAction
285 *
286 * @return array
287 */
00be9182 288 public function activityTypes($activityTypesXML, $maxInst = FALSE, $isLabel = FALSE, $maskAction = FALSE) {
6a488035 289 $activityTypes = &$this->allActivityTypes(TRUE, TRUE);
be2fb01f 290 $result = [];
6a488035
TO
291 foreach ($activityTypesXML as $activityTypeXML) {
292 foreach ($activityTypeXML as $recordXML) {
293 $activityTypeName = (string ) $recordXML->name;
353ffa53 294 $maxInstances = (string ) $recordXML->max_instances;
6a488035
TO
295 $activityTypeInfo = CRM_Utils_Array::value($activityTypeName, $activityTypes);
296
297 if ($activityTypeInfo['id']) {
298 if ($maskAction) {
299 if ($maskAction == 'edit' && '0' === (string ) $recordXML->editable) {
300 $result[$maskAction][] = $activityTypeInfo['id'];
301 }
302 }
303 else {
304 if (!$maxInst) {
305 //if we want,labels of activities should be returned.
306 if ($isLabel) {
307 $result[$activityTypeInfo['id']] = $activityTypeInfo['label'];
308 }
309 else {
310 $result[$activityTypeInfo['id']] = $activityTypeName;
311 }
312 }
313 else {
314 if ($maxInstances) {
315 $result[$activityTypeName] = $maxInstances;
316 }
317 }
318 }
319 }
320 }
321 }
322
323 // call option value hook
324 CRM_Utils_Hook::optionValues($result, 'case_activity_type');
325
326 return $result;
327 }
328
e19323c9
TO
329 /**
330 * @param SimpleXMLElement $caseTypeXML
ad8d1ce3 331 *
e19323c9
TO
332 * @return array<string> symbolic activity-type names
333 */
00be9182 334 public function getDeclaredActivityTypes($caseTypeXML) {
be2fb01f 335 $result = [];
83151a3f
DJ
336
337 if (!empty($caseTypeXML->ActivityTypes) && $caseTypeXML->ActivityTypes->ActivityType) {
338 foreach ($caseTypeXML->ActivityTypes->ActivityType as $activityTypeXML) {
339 $result[] = (string) $activityTypeXML->name;
e19323c9 340 }
83151a3f 341 }
e19323c9 342
83151a3f
DJ
343 if (!empty($caseTypeXML->ActivitySets) && $caseTypeXML->ActivitySets->ActivitySet) {
344 foreach ($caseTypeXML->ActivitySets->ActivitySet as $activitySetXML) {
345 if ($activitySetXML->ActivityTypes && $activitySetXML->ActivityTypes->ActivityType) {
346 foreach ($activitySetXML->ActivityTypes->ActivityType as $activityTypeXML) {
347 $result[] = (string) $activityTypeXML->name;
e19323c9
TO
348 }
349 }
350 }
351 }
83151a3f 352
e19323c9
TO
353 $result = array_unique($result);
354 sort($result);
355 return $result;
356 }
357
358 /**
d0a94888
AF
359 * Relationships are straight from XML, described from perspective of non-client
360 *
e19323c9 361 * @param SimpleXMLElement $caseTypeXML
ad8d1ce3 362 *
e19323c9
TO
363 * @return array<string> symbolic relationship-type names
364 */
00be9182 365 public function getDeclaredRelationshipTypes($caseTypeXML) {
be2fb01f 366 $result = [];
83151a3f
DJ
367
368 if (!empty($caseTypeXML->CaseRoles) && $caseTypeXML->CaseRoles->RelationshipType) {
369 foreach ($caseTypeXML->CaseRoles->RelationshipType as $relTypeXML) {
370 $result[] = (string) $relTypeXML->name;
e19323c9
TO
371 }
372 }
83151a3f 373
e19323c9
TO
374 $result = array_unique($result);
375 sort($result);
376 return $result;
377 }
378
4c6ce474 379 /**
c490a46a 380 * @param array $params
4c6ce474 381 */
00be9182 382 public function deleteEmptyActivity(&$params) {
44f817d4 383 $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
9e74e3ce 384 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
8ef12e64 385
6a488035
TO
386 $query = "
387DELETE a
388FROM civicrm_activity a
91da6cd5 389INNER JOIN civicrm_activity_contact t ON t.activity_id = a.id
97875df1 390INNER JOIN civicrm_case_activity ca on ca.activity_id = a.id
91da6cd5 391WHERE t.contact_id = %1
9e74e3ce 392AND t.record_type_id = $targetID
6a488035
TO
393AND a.is_auto = 1
394AND a.is_current_revision = 1
97875df1 395AND ca.case_id = %2
6a488035 396";
be2fb01f 397 $sqlParams = [1 => [$params['clientID'], 'Integer'], 2 => [$params['caseID'], 'Integer']];
6a488035
TO
398 CRM_Core_DAO::executeQuery($query, $sqlParams);
399 }
400
4c6ce474 401 /**
c490a46a 402 * @param array $params
4c6ce474
EM
403 *
404 * @return bool
405 */
00be9182 406 public function isActivityPresent(&$params) {
6a488035
TO
407 $query = "
408SELECT count(a.id)
409FROM civicrm_activity a
410INNER JOIN civicrm_case_activity ca on ca.activity_id = a.id
411WHERE a.activity_type_id = %1
412AND ca.case_id = %2
413AND a.is_deleted = 0
414";
415
be2fb01f
CW
416 $sqlParams = [
417 1 => [$params['activityTypeID'], 'Integer'],
418 2 => [$params['caseID'], 'Integer'],
419 ];
6a488035
TO
420 $count = CRM_Core_DAO::singleValueQuery($query, $sqlParams);
421
422 // check for max instance
6f55186b 423 $caseType = CRM_Case_BAO_Case::getCaseType($params['caseID'], 'name');
6a488035
TO
424 $maxInstance = self::getMaxInstance($caseType, $params['activityTypeName']);
425
426 return $maxInstance ? ($count < $maxInstance ? FALSE : TRUE) : FALSE;
427 }
428
4c6ce474
EM
429 /**
430 * @param $activityTypeXML
c490a46a 431 * @param array $params
4c6ce474
EM
432 *
433 * @return bool
434 * @throws CRM_Core_Exception
435 * @throws Exception
436 */
00be9182 437 public function createActivity($activityTypeXML, &$params) {
6a488035 438 $activityTypeName = (string) $activityTypeXML->name;
353ffa53 439 $activityTypes = &$this->allActivityTypes(TRUE, TRUE);
6a488035
TO
440 $activityTypeInfo = CRM_Utils_Array::value($activityTypeName, $activityTypes);
441
442 if (!$activityTypeInfo) {
6b02fdc3 443 $docLink = CRM_Utils_System::docURL2("user/case-management/set-up");
6a488035 444 CRM_Core_Error::fatal(ts('Activity type %1, found in case configuration file, is not present in the database %2',
be2fb01f 445 [1 => $activityTypeName, 2 => $docLink]
353ffa53 446 ));
6a488035
TO
447 return FALSE;
448 }
449
450 $activityTypeID = $activityTypeInfo['id'];
451
452 if (isset($activityTypeXML->status)) {
453 $statusName = (string) $activityTypeXML->status;
454 }
455 else {
456 $statusName = 'Scheduled';
457 }
458
10befc1f 459 $client = (array) $params['clientID'];
6a488035
TO
460
461 //set order
462 $orderVal = '';
463 if (isset($activityTypeXML->order)) {
464 $orderVal = (string) $activityTypeXML->order;
465 }
466
467 if ($activityTypeName == 'Open Case') {
be2fb01f 468 $activityParams = [
6a488035
TO
469 'activity_type_id' => $activityTypeID,
470 'source_contact_id' => $params['creatorID'],
471 'is_auto' => FALSE,
472 'is_current_revision' => 1,
473 'subject' => CRM_Utils_Array::value('subject', $params) ? $params['subject'] : $activityTypeName,
d66c61b6 474 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', $statusName),
6a488035
TO
475 'target_contact_id' => $client,
476 'medium_id' => CRM_Utils_Array::value('medium_id', $params),
477 'location' => CRM_Utils_Array::value('location', $params),
478 'details' => CRM_Utils_Array::value('details', $params),
479 'duration' => CRM_Utils_Array::value('duration', $params),
480 'weight' => $orderVal,
be2fb01f 481 ];
6a488035
TO
482 }
483 else {
be2fb01f 484 $activityParams = [
6a488035
TO
485 'activity_type_id' => $activityTypeID,
486 'source_contact_id' => $params['creatorID'],
487 'is_auto' => TRUE,
488 'is_current_revision' => 1,
d66c61b6 489 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', $statusName),
6a488035
TO
490 'target_contact_id' => $client,
491 'weight' => $orderVal,
be2fb01f 492 ];
6a488035
TO
493 }
494
ad8d1ce3
RO
495 $activityParams['assignee_contact_id'] = $this->getDefaultAssigneeForActivity($activityParams, $activityTypeXML);
496
6a488035
TO
497 //parsing date to default preference format
498 $params['activity_date_time'] = CRM_Utils_Date::processDate($params['activity_date_time']);
499
500 if ($activityTypeName == 'Open Case') {
501 // we don't set activity_date_time for auto generated
502 // activities, but we want it to be set for open case.
503 $activityParams['activity_date_time'] = $params['activity_date_time'];
504 if (array_key_exists('custom', $params) && is_array($params['custom'])) {
505 $activityParams['custom'] = $params['custom'];
506 }
507
508 // Add parameters for attachments
509
aaffa79f 510 $numAttachments = Civi::settings()->get('max_attachments');
481a74f4 511 for ($i = 1; $i <= $numAttachments; $i++) {
6a488035 512 $attachName = "attachFile_$i";
481a74f4 513 if (isset($params[$attachName]) && !empty($params[$attachName])) {
6a488035
TO
514 $activityParams[$attachName] = $params[$attachName];
515 }
516 }
517 }
518 else {
519 $activityDate = NULL;
520 //get date of reference activity if set.
521 if ($referenceActivityName = (string) $activityTypeXML->reference_activity) {
522
523 //we skip open case as reference activity.CRM-4374.
a7488080 524 if (!empty($params['resetTimeline']) && $referenceActivityName == 'Open Case') {
6a488035
TO
525 $activityDate = $params['activity_date_time'];
526 }
527 else {
528 $referenceActivityInfo = CRM_Utils_Array::value($referenceActivityName, $activityTypes);
529 if ($referenceActivityInfo['id']) {
be2fb01f 530 $caseActivityParams = ['activity_type_id' => $referenceActivityInfo['id']];
6a488035
TO
531
532 //if reference_select is set take according activity.
533 if ($referenceSelect = (string) $activityTypeXML->reference_select) {
534 $caseActivityParams[$referenceSelect] = 1;
535 }
536
537 $referenceActivity = CRM_Case_BAO_Case::getCaseActivityDates($params['caseID'], $caseActivityParams, TRUE);
538
539 if (is_array($referenceActivity)) {
540 foreach ($referenceActivity as $aId => $details) {
541 $activityDate = CRM_Utils_Array::value('activity_date', $details);
542 break;
543 }
544 }
545 }
546 }
547 }
548 if (!$activityDate) {
549 $activityDate = $params['activity_date_time'];
550 }
551 list($activity_date, $activity_time) = CRM_Utils_Date::setDateDefaults($activityDate);
552 $activityDateTime = CRM_Utils_Date::processDate($activity_date, $activity_time);
553 //add reference offset to date.
554 if ((int) $activityTypeXML->reference_offset) {
555 $activityDateTime = CRM_Utils_Date::intervalAdd('day', (int) $activityTypeXML->reference_offset,
556 $activityDateTime
557 );
558 }
559
560 $activityParams['activity_date_time'] = CRM_Utils_Date::format($activityDateTime);
561 }
562
563 // if same activity is already there, skip and dont touch
564 $params['activityTypeID'] = $activityTypeID;
565 $params['activityTypeName'] = $activityTypeName;
566 if ($this->isActivityPresent($params)) {
567 return TRUE;
568 }
569 $activityParams['case_id'] = $params['caseID'];
a7488080 570 if (!empty($activityParams['is_auto'])) {
6a488035
TO
571 $activityParams['skipRecentView'] = TRUE;
572 }
573
d66c61b6 574 // @todo - switch to using api & remove the parameter pre-wrangling above.
6a488035
TO
575 $activity = CRM_Activity_BAO_Activity::create($activityParams);
576
577 if (!$activity) {
578 CRM_Core_Error::fatal();
579 return FALSE;
580 }
581
582 // create case activity record
be2fb01f 583 $caseParams = [
6a488035
TO
584 'activity_id' => $activity->id,
585 'case_id' => $params['caseID'],
be2fb01f 586 ];
6a488035
TO
587 CRM_Case_BAO_Case::processCaseActivity($caseParams);
588 return TRUE;
589 }
590
ad8d1ce3
RO
591 /**
592 * Return the default assignee contact for the activity.
593 *
594 * @param array $activityParams
595 * @param object $activityTypeXML
596 *
597 * @return int|null the ID of the default assignee contact or null if none.
598 */
599 protected function getDefaultAssigneeForActivity($activityParams, $activityTypeXML) {
600 if (!isset($activityTypeXML->default_assignee_type)) {
601 return NULL;
602 }
603
604 $defaultAssigneeOptionsValues = $this->getDefaultAssigneeOptionValues();
605
606 switch ($activityTypeXML->default_assignee_type) {
607 case $defaultAssigneeOptionsValues['BY_RELATIONSHIP']:
608 return $this->getDefaultAssigneeByRelationship($activityParams, $activityTypeXML);
609
610 break;
611 case $defaultAssigneeOptionsValues['SPECIFIC_CONTACT']:
612 return $this->getDefaultAssigneeBySpecificContact($activityTypeXML);
613
614 break;
615 case $defaultAssigneeOptionsValues['USER_CREATING_THE_CASE']:
616 return $activityParams['source_contact_id'];
617
618 break;
619 case $defaultAssigneeOptionsValues['NONE']:
620 default:
621 return NULL;
622 }
623 }
624
625 /**
626 * Fetches and caches the activity's default assignee options.
627 *
628 * @return array
629 */
630 protected function getDefaultAssigneeOptionValues() {
631 if (!empty($this->defaultAssigneeOptionsValues)) {
632 return $this->defaultAssigneeOptionsValues;
633 }
634
635 $defaultAssigneeOptions = civicrm_api3('OptionValue', 'get', [
636 'option_group_id' => 'activity_default_assignee',
f157740d 637 'options' => ['limit' => 0],
ad8d1ce3
RO
638 ]);
639
640 foreach ($defaultAssigneeOptions['values'] as $option) {
641 $this->defaultAssigneeOptionsValues[$option['name']] = $option['value'];
642 }
643
644 return $this->defaultAssigneeOptionsValues;
645 }
646
647 /**
648 * Returns the default assignee for the activity by searching for the target's
649 * contact relationship type defined in the activity's details.
650 *
651 * @param array $activityParams
652 * @param object $activityTypeXML
653 *
654 * @return int|null the ID of the default assignee contact or null if none.
655 */
656 protected function getDefaultAssigneeByRelationship($activityParams, $activityTypeXML) {
68098e7b
RO
657 $isDefaultRelationshipDefined = isset($activityTypeXML->default_assignee_relationship)
658 && preg_match('/\d+_[ab]_[ab]/', $activityTypeXML->default_assignee_relationship);
659
660 if (!$isDefaultRelationshipDefined) {
ad8d1ce3
RO
661 return NULL;
662 }
663
664 $targetContactId = is_array($activityParams['target_contact_id'])
665 ? CRM_Utils_Array::first($activityParams['target_contact_id'])
666 : $activityParams['target_contact_id'];
68098e7b 667 list($relTypeId, $a, $b) = explode('_', $activityTypeXML->default_assignee_relationship);
ad8d1ce3 668
68098e7b
RO
669 $params = [
670 'relationship_type_id' => $relTypeId,
671 "contact_id_$b" => $targetContactId,
ad8d1ce3 672 'is_active' => 1,
68098e7b
RO
673 ];
674
675 if ($this->isBidirectionalRelationshipType($relTypeId)) {
676 $params["contact_id_$a"] = $targetContactId;
677 $params['options']['or'] = [['contact_id_a', 'contact_id_b']];
678 }
679
680 $relationships = civicrm_api3('Relationship', 'get', $params);
ad8d1ce3
RO
681
682 if ($relationships['count']) {
68098e7b
RO
683 $relationship = CRM_Utils_Array::first($relationships['values']);
684
685 // returns the contact id on the other side of the relationship:
686 return (int) $relationship['contact_id_a'] === (int) $targetContactId
687 ? $relationship['contact_id_b']
688 : $relationship['contact_id_a'];
ad8d1ce3
RO
689 }
690 else {
691 return NULL;
692 }
693 }
694
68098e7b
RO
695 /**
696 * Determines if the given relationship type is bidirectional or not by
697 * comparing their labels.
698 *
699 * @return bool
700 */
701 protected function isBidirectionalRelationshipType($relationshipTypeId) {
702 $relationshipTypeResult = civicrm_api3('RelationshipType', 'get', [
703 'id' => $relationshipTypeId,
f157740d 704 'options' => ['limit' => 1],
68098e7b
RO
705 ]);
706
707 if ($relationshipTypeResult['count'] === 0) {
708 return FALSE;
709 }
710
711 $relationshipType = CRM_Utils_Array::first($relationshipTypeResult['values']);
712
713 return $relationshipType['label_b_a'] === $relationshipType['label_a_b'];
714 }
715
ad8d1ce3
RO
716 /**
717 * Returns the activity's default assignee for a specific contact if the contact exists,
718 * otherwise returns null.
719 *
720 * @param object $activityTypeXML
721 *
722 * @return int|null
723 */
724 protected function getDefaultAssigneeBySpecificContact($activityTypeXML) {
725 if (!$activityTypeXML->default_assignee_contact) {
726 return NULL;
727 }
728
729 $contact = civicrm_api3('Contact', 'get', [
f157740d 730 'id' => $activityTypeXML->default_assignee_contact,
ad8d1ce3
RO
731 ]);
732
733 if ($contact['count'] == 1) {
734 return $activityTypeXML->default_assignee_contact;
735 }
736
737 return NULL;
738 }
739
4c6ce474
EM
740 /**
741 * @param $activitySetsXML
742 *
743 * @return array
744 */
00be9182 745 public static function activitySets($activitySetsXML) {
be2fb01f 746 $result = [];
6a488035
TO
747 foreach ($activitySetsXML as $activitySetXML) {
748 foreach ($activitySetXML as $recordXML) {
749 $activitySetName = (string ) $recordXML->name;
750 $activitySetLabel = (string ) $recordXML->label;
751 $result[$activitySetName] = $activitySetLabel;
752 }
753 }
754
755 return $result;
756 }
757
4c6ce474
EM
758 /**
759 * @param $caseType
760 * @param null $activityTypeName
761 *
762 * @return array|bool|mixed
763 * @throws Exception
764 */
00be9182 765 public function getMaxInstance($caseType, $activityTypeName = NULL) {
6a488035
TO
766 $xml = $this->retrieve($caseType);
767
768 if ($xml === FALSE) {
769 CRM_Core_Error::fatal();
770 return FALSE;
771 }
772
773 $activityInstances = $this->activityTypes($xml->ActivityTypes, TRUE);
774 return $activityTypeName ? CRM_Utils_Array::value($activityTypeName, $activityInstances) : $activityInstances;
775 }
776
4c6ce474
EM
777 /**
778 * @param $caseType
779 *
780 * @return array|mixed
781 */
00be9182 782 public function getCaseManagerRoleId($caseType) {
6a488035
TO
783 $xml = $this->retrieve($caseType);
784 return $this->caseRoles($xml->CaseRoles, TRUE);
785 }
786
708d8fa2
TO
787 /**
788 * @param string $caseType
ad8d1ce3 789 *
708d8fa2
TO
790 * @return array<\Civi\CCase\CaseChangeListener>
791 */
00be9182 792 public function getListeners($caseType) {
708d8fa2 793 $xml = $this->retrieve($caseType);
be2fb01f 794 $listeners = [];
708d8fa2
TO
795 if ($xml->Listeners && $xml->Listeners->Listener) {
796 foreach ($xml->Listeners->Listener as $listenerXML) {
797 $class = (string) $listenerXML;
798 $listeners[] = new $class();
799 }
800 }
801 return $listeners;
802 }
803
4c6ce474
EM
804 /**
805 * @return int
806 */
00be9182 807 public function getRedactActivityEmail() {
23d6731b 808 return $this->getBoolSetting('civicaseRedactActivityEmail', 'RedactActivityEmail');
6a488035
TO
809 }
810
811 /**
fe482240 812 * Retrieves AllowMultipleCaseClients setting.
6a488035 813 *
a6c01b45
CW
814 * @return string
815 * 1 if allowed, 0 if not
6a488035 816 */
00be9182 817 public function getAllowMultipleCaseClients() {
c8fd28dc 818 return $this->getBoolSetting('civicaseAllowMultipleClients', 'AllowMultipleCaseClients');
6a488035
TO
819 }
820
821 /**
fe482240 822 * Retrieves NaturalActivityTypeSort setting.
6a488035 823 *
a6c01b45
CW
824 * @return string
825 * 1 if natural, 0 if alphabetic
6a488035 826 */
00be9182 827 public function getNaturalActivityTypeSort() {
23d6731b
TO
828 return $this->getBoolSetting('civicaseNaturalActivityTypeSort', 'NaturalActivityTypeSort');
829 }
830
831 /**
832 * @param string $settingKey
833 * @param string $xmlTag
834 * @param mixed $default
ad8d1ce3 835 *
23d6731b
TO
836 * @return int
837 */
838 private function getBoolSetting($settingKey, $xmlTag, $default = 0) {
839 $setting = Civi::settings()->get($settingKey);
ec61a2b2 840 if ($setting !== 'default') {
d6742e0f
TO
841 return (int) $setting;
842 }
23d6731b
TO
843 if ($xml = $this->retrieve("Settings")) {
844 return (string) $xml->{$xmlTag} ? 1 : 0;
845 }
846 return $default;
6a488035 847 }
96025800 848
6a488035 849}