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