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