Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
bc77d7c0 | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
6a488035 | 5 | | | |
bc77d7c0 TO |
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 | | |
6a488035 | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
6a488035 TO |
11 | |
12 | /** | |
13 | * | |
14 | * @package CRM | |
ca5cec67 | 15 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
6a488035 TO |
16 | */ |
17 | class CRM_Case_XMLProcessor_Process extends CRM_Case_XMLProcessor { | |
ad8d1ce3 RO |
18 | protected $defaultAssigneeOptionsValues = []; |
19 | ||
4c6ce474 | 20 | /** |
67d19299 | 21 | * Run. |
22 | * | |
23 | * @param string $caseType | |
c490a46a | 24 | * @param array $params |
4c6ce474 | 25 | * |
b5834543 | 26 | * @throws CRM_Core_Exception |
4c6ce474 | 27 | */ |
00be9182 | 28 | public function run($caseType, &$params) { |
6a488035 TO |
29 | $xml = $this->retrieve($caseType); |
30 | ||
31 | if ($xml === FALSE) { | |
6b02fdc3 | 32 | $docLink = CRM_Utils_System::docURL2("user/case-management/set-up"); |
b5834543 | 33 | throw new CRM_Core_Exception(ts("Configuration file could not be retrieved for case type = '%1' %2.", |
be2fb01f | 34 | [1 => $caseType, 2 => $docLink] |
353ffa53 | 35 | )); |
6a488035 TO |
36 | } |
37 | ||
38 | $xmlProcessorProcess = new CRM_Case_XMLProcessor_Process(); | |
39 | $this->_isMultiClient = $xmlProcessorProcess->getAllowMultipleCaseClients(); | |
40 | ||
41 | $this->process($xml, $params); | |
42 | } | |
43 | ||
4c6ce474 EM |
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 | */ | |
00be9182 | 53 | public function get($caseType, $fieldSet, $isLabel = FALSE, $maskAction = FALSE) { |
6a488035 TO |
54 | $xml = $this->retrieve($caseType); |
55 | if ($xml === FALSE) { | |
6b02fdc3 | 56 | $docLink = CRM_Utils_System::docURL2("user/case-management/set-up"); |
b5834543 | 57 | throw new CRM_Core_Exception(ts("Unable to load configuration file for the referenced case type: '%1' %2.", |
be2fb01f | 58 | [1 => $caseType, 2 => $docLink] |
353ffa53 | 59 | )); |
6a488035 TO |
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 | ||
4c6ce474 EM |
74 | /** |
75 | * @param $xml | |
c490a46a | 76 | * @param array $params |
4c6ce474 EM |
77 | * |
78 | * @throws Exception | |
79 | */ | |
00be9182 | 80 | public function process($xml, &$params) { |
9c1bc317 CW |
81 | $standardTimeline = $params['standardTimeline'] ?? NULL; |
82 | $activitySetName = $params['activitySetName'] ?? NULL; | |
6a488035 TO |
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) { | |
2ab37b05 | 88 | if ($relationshipTypeXML->creator) { |
2058bf54 | 89 | if (!$this->createRelationships($relationshipTypeXML, |
353ffa53 TO |
90 | $params |
91 | ) | |
92 | ) { | |
b5834543 | 93 | throw new CRM_Core_Exception('Unable to create case relationships'); |
6a488035 TO |
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) { | |
2ab37b05 | 108 | if ($activitySetXML->timeline) { |
109 | return $this->processStandardTimeline($activitySetXML, $params); | |
6a488035 TO |
110 | } |
111 | } | |
112 | elseif ($activitySetName) { | |
2ab37b05 | 113 | $name = (string) $activitySetXML->name; |
6a488035 | 114 | if ($name == $activitySetName) { |
2ab37b05 | 115 | return $this->processActivitySet($activitySetXML, $params); |
6a488035 TO |
116 | } |
117 | } | |
118 | } | |
119 | } | |
120 | } | |
121 | ||
4c6ce474 EM |
122 | /** |
123 | * @param $activitySetXML | |
c490a46a | 124 | * @param array $params |
4c6ce474 | 125 | */ |
00be9182 | 126 | public function processStandardTimeline($activitySetXML, &$params) { |
448e7600 | 127 | if ('Change Case Type' == CRM_Utils_Array::value('activityTypeName', $params) |
353ffa53 TO |
128 | && CRM_Utils_Array::value('resetTimeline', $params, TRUE) |
129 | ) { | |
6a488035 TO |
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 | ||
4c6ce474 EM |
141 | /** |
142 | * @param $activitySetXML | |
c490a46a | 143 | * @param array $params |
4c6ce474 | 144 | */ |
00be9182 | 145 | public function processActivitySet($activitySetXML, &$params) { |
6a488035 TO |
146 | foreach ($activitySetXML->ActivityTypes as $activityTypesXML) { |
147 | foreach ($activityTypesXML as $activityTypeXML) { | |
148 | $this->createActivity($activityTypeXML, $params); | |
149 | } | |
150 | } | |
151 | } | |
152 | ||
4c6ce474 EM |
153 | /** |
154 | * @param $caseRolesXML | |
155 | * @param bool $isCaseManager | |
156 | * | |
157 | * @return array|mixed | |
158 | */ | |
00be9182 | 159 | public function &caseRoles($caseRolesXML, $isCaseManager = FALSE) { |
d0a94888 AF |
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) | |
d0a94888 | 163 | $relationshipTypesToReturn = &$this->allRelationshipTypes(FALSE); |
6a488035 | 164 | |
be2fb01f | 165 | $result = []; |
6a488035 TO |
166 | foreach ($caseRolesXML as $caseRoleXML) { |
167 | foreach ($caseRoleXML->RelationshipType as $relationshipTypeXML) { | |
2058bf54 | 168 | list($relationshipTypeID,) = $this->locateNameOrLabel($relationshipTypeXML); |
6a488035 TO |
169 | if ($relationshipTypeID === FALSE) { |
170 | continue; | |
171 | } | |
172 | ||
173 | if (!$isCaseManager) { | |
d0a94888 | 174 | $result[$relationshipTypeID] = $relationshipTypesToReturn[$relationshipTypeID]; |
6a488035 | 175 | } |
913b5673 | 176 | elseif ($relationshipTypeXML->manager == 1) { |
6a488035 TO |
177 | return $relationshipTypeID; |
178 | } | |
179 | } | |
180 | } | |
181 | return $result; | |
182 | } | |
183 | ||
4c6ce474 | 184 | /** |
2058bf54 | 185 | * @param SimpleXMLElement $relationshipTypeXML |
c490a46a | 186 | * @param array $params |
4c6ce474 EM |
187 | * |
188 | * @return bool | |
ac15829d | 189 | * @throws CRM_Core_Exception |
4c6ce474 | 190 | */ |
2058bf54 | 191 | public function createRelationships($relationshipTypeXML, &$params) { |
6a488035 | 192 | |
2058bf54 D |
193 | // get the relationship |
194 | list($relationshipType, $relationshipTypeName) = $this->locateNameOrLabel($relationshipTypeXML); | |
41cf58d3 | 195 | if ($relationshipType === FALSE) { |
6b02fdc3 | 196 | $docLink = CRM_Utils_System::docURL2("user/case-management/set-up"); |
b5834543 | 197 | throw new CRM_Core_Exception(ts('Relationship type %1, found in case configuration file, is not present in the database %2', |
be2fb01f | 198 | [1 => $relationshipTypeName, 2 => $docLink] |
353ffa53 | 199 | )); |
6a488035 TO |
200 | } |
201 | ||
202 | $client = $params['clientID']; | |
203 | if (!is_array($client)) { | |
be2fb01f | 204 | $client = [$client]; |
6a488035 TO |
205 | } |
206 | ||
207 | foreach ($client as $key => $clientId) { | |
be2fb01f | 208 | $relationshipParams = [ |
41cf58d3 | 209 | 'relationship_type_id' => substr($relationshipType, 0, -4), |
6a488035 TO |
210 | 'is_active' => 1, |
211 | 'case_id' => $params['caseID'], | |
212 | 'start_date' => date("Ymd"), | |
6b409353 | 213 | 'end_date' => $params['relationship_end_date'] ?? NULL, |
be2fb01f | 214 | ]; |
6a488035 | 215 | |
41cf58d3 AF |
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 | ||
6a488035 | 225 | if (!$this->createRelationship($relationshipParams)) { |
b5834543 | 226 | throw new CRM_Core_Exception('Unable to create case relationship'); |
6a488035 TO |
227 | } |
228 | } | |
229 | return TRUE; | |
230 | } | |
231 | ||
4c6ce474 | 232 | /** |
c490a46a | 233 | * @param array $params |
4c6ce474 EM |
234 | * |
235 | * @return bool | |
236 | */ | |
00be9182 | 237 | public function createRelationship(&$params) { |
6a488035 TO |
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 | ||
4c6ce474 EM |
247 | /** |
248 | * @param $activityTypesXML | |
249 | * @param bool $maxInst | |
250 | * @param bool $isLabel | |
251 | * @param bool $maskAction | |
252 | * | |
253 | * @return array | |
254 | */ | |
00be9182 | 255 | public function activityTypes($activityTypesXML, $maxInst = FALSE, $isLabel = FALSE, $maskAction = FALSE) { |
72a3d8bc | 256 | $activityTypes = CRM_Case_PseudoConstant::caseActivityType(TRUE, TRUE); |
be2fb01f | 257 | $result = []; |
6a488035 TO |
258 | foreach ($activityTypesXML as $activityTypeXML) { |
259 | foreach ($activityTypeXML as $recordXML) { | |
2ab37b05 | 260 | $activityTypeName = (string) $recordXML->name; |
261 | $maxInstances = (string) $recordXML->max_instances; | |
9c1bc317 | 262 | $activityTypeInfo = $activityTypes[$activityTypeName] ?? NULL; |
6a488035 TO |
263 | |
264 | if ($activityTypeInfo['id']) { | |
265 | if ($maskAction) { | |
2ab37b05 | 266 | if ($maskAction == 'edit' && '0' === (string) $recordXML->editable) { |
6a488035 TO |
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 | ||
e19323c9 TO |
296 | /** |
297 | * @param SimpleXMLElement $caseTypeXML | |
ad8d1ce3 | 298 | * |
e19323c9 TO |
299 | * @return array<string> symbolic activity-type names |
300 | */ | |
00be9182 | 301 | public function getDeclaredActivityTypes($caseTypeXML) { |
be2fb01f | 302 | $result = []; |
83151a3f DJ |
303 | |
304 | if (!empty($caseTypeXML->ActivityTypes) && $caseTypeXML->ActivityTypes->ActivityType) { | |
305 | foreach ($caseTypeXML->ActivityTypes->ActivityType as $activityTypeXML) { | |
306 | $result[] = (string) $activityTypeXML->name; | |
e19323c9 | 307 | } |
83151a3f | 308 | } |
e19323c9 | 309 | |
83151a3f DJ |
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; | |
e19323c9 TO |
315 | } |
316 | } | |
317 | } | |
318 | } | |
83151a3f | 319 | |
e19323c9 TO |
320 | $result = array_unique($result); |
321 | sort($result); | |
322 | return $result; | |
323 | } | |
324 | ||
325 | /** | |
d0a94888 AF |
326 | * Relationships are straight from XML, described from perspective of non-client |
327 | * | |
e19323c9 | 328 | * @param SimpleXMLElement $caseTypeXML |
ad8d1ce3 | 329 | * |
e19323c9 TO |
330 | * @return array<string> symbolic relationship-type names |
331 | */ | |
00be9182 | 332 | public function getDeclaredRelationshipTypes($caseTypeXML) { |
be2fb01f | 333 | $result = []; |
83151a3f DJ |
334 | |
335 | if (!empty($caseTypeXML->CaseRoles) && $caseTypeXML->CaseRoles->RelationshipType) { | |
336 | foreach ($caseTypeXML->CaseRoles->RelationshipType as $relTypeXML) { | |
2058bf54 D |
337 | list(, $relationshipTypeMachineName) = $this->locateNameOrLabel($relTypeXML); |
338 | $result[] = $relationshipTypeMachineName; | |
e19323c9 TO |
339 | } |
340 | } | |
83151a3f | 341 | |
e19323c9 TO |
342 | $result = array_unique($result); |
343 | sort($result); | |
344 | return $result; | |
345 | } | |
346 | ||
4c6ce474 | 347 | /** |
c490a46a | 348 | * @param array $params |
4c6ce474 | 349 | */ |
00be9182 | 350 | public function deleteEmptyActivity(&$params) { |
44f817d4 | 351 | $activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate'); |
9e74e3ce | 352 | $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); |
8ef12e64 | 353 | |
6a488035 TO |
354 | $query = " |
355 | DELETE a | |
356 | FROM civicrm_activity a | |
91da6cd5 | 357 | INNER JOIN civicrm_activity_contact t ON t.activity_id = a.id |
97875df1 | 358 | INNER JOIN civicrm_case_activity ca on ca.activity_id = a.id |
91da6cd5 | 359 | WHERE t.contact_id = %1 |
9e74e3ce | 360 | AND t.record_type_id = $targetID |
6a488035 TO |
361 | AND a.is_auto = 1 |
362 | AND a.is_current_revision = 1 | |
97875df1 | 363 | AND ca.case_id = %2 |
6a488035 | 364 | "; |
be2fb01f | 365 | $sqlParams = [1 => [$params['clientID'], 'Integer'], 2 => [$params['caseID'], 'Integer']]; |
6a488035 TO |
366 | CRM_Core_DAO::executeQuery($query, $sqlParams); |
367 | } | |
368 | ||
4c6ce474 | 369 | /** |
c490a46a | 370 | * @param array $params |
4c6ce474 EM |
371 | * |
372 | * @return bool | |
373 | */ | |
00be9182 | 374 | public function isActivityPresent(&$params) { |
6a488035 TO |
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 | ||
be2fb01f CW |
384 | $sqlParams = [ |
385 | 1 => [$params['activityTypeID'], 'Integer'], | |
386 | 2 => [$params['caseID'], 'Integer'], | |
387 | ]; | |
6a488035 TO |
388 | $count = CRM_Core_DAO::singleValueQuery($query, $sqlParams); |
389 | ||
390 | // check for max instance | |
6f55186b | 391 | $caseType = CRM_Case_BAO_Case::getCaseType($params['caseID'], 'name'); |
6a488035 TO |
392 | $maxInstance = self::getMaxInstance($caseType, $params['activityTypeName']); |
393 | ||
394 | return $maxInstance ? ($count < $maxInstance ? FALSE : TRUE) : FALSE; | |
395 | } | |
396 | ||
4c6ce474 EM |
397 | /** |
398 | * @param $activityTypeXML | |
c490a46a | 399 | * @param array $params |
4c6ce474 EM |
400 | * |
401 | * @return bool | |
402 | * @throws CRM_Core_Exception | |
403 | * @throws Exception | |
404 | */ | |
00be9182 | 405 | public function createActivity($activityTypeXML, &$params) { |
6a488035 | 406 | $activityTypeName = (string) $activityTypeXML->name; |
69805643 | 407 | $activityTypes = CRM_Case_PseudoConstant::caseActivityType(TRUE, TRUE); |
9c1bc317 | 408 | $activityTypeInfo = $activityTypes[$activityTypeName] ?? NULL; |
6a488035 TO |
409 | |
410 | if (!$activityTypeInfo) { | |
6b02fdc3 | 411 | $docLink = CRM_Utils_System::docURL2("user/case-management/set-up"); |
b5834543 | 412 | throw new CRM_Core_Exception(ts('Activity type %1, found in case configuration file, is not present in the database %2', |
be2fb01f | 413 | [1 => $activityTypeName, 2 => $docLink] |
353ffa53 | 414 | )); |
6a488035 TO |
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 | ||
10befc1f | 426 | $client = (array) $params['clientID']; |
6a488035 TO |
427 | |
428 | //set order | |
429 | $orderVal = ''; | |
430 | if (isset($activityTypeXML->order)) { | |
431 | $orderVal = (string) $activityTypeXML->order; | |
432 | } | |
433 | ||
434 | if ($activityTypeName == 'Open Case') { | |
be2fb01f | 435 | $activityParams = [ |
6a488035 TO |
436 | 'activity_type_id' => $activityTypeID, |
437 | 'source_contact_id' => $params['creatorID'], | |
438 | 'is_auto' => FALSE, | |
439 | 'is_current_revision' => 1, | |
e71c1326 | 440 | 'subject' => !empty($params['subject']) ? $params['subject'] : $activityTypeName, |
d66c61b6 | 441 | 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', $statusName), |
6a488035 | 442 | 'target_contact_id' => $client, |
6b409353 CW |
443 | 'medium_id' => $params['medium_id'] ?? NULL, |
444 | 'location' => $params['location'] ?? NULL, | |
445 | 'details' => $params['details'] ?? NULL, | |
446 | 'duration' => $params['duration'] ?? NULL, | |
6a488035 | 447 | 'weight' => $orderVal, |
be2fb01f | 448 | ]; |
6a488035 TO |
449 | } |
450 | else { | |
be2fb01f | 451 | $activityParams = [ |
6a488035 TO |
452 | 'activity_type_id' => $activityTypeID, |
453 | 'source_contact_id' => $params['creatorID'], | |
454 | 'is_auto' => TRUE, | |
455 | 'is_current_revision' => 1, | |
d66c61b6 | 456 | 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', $statusName), |
6a488035 TO |
457 | 'target_contact_id' => $client, |
458 | 'weight' => $orderVal, | |
be2fb01f | 459 | ]; |
6a488035 TO |
460 | } |
461 | ||
ad8d1ce3 RO |
462 | $activityParams['assignee_contact_id'] = $this->getDefaultAssigneeForActivity($activityParams, $activityTypeXML); |
463 | ||
6a488035 TO |
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 | ||
aaffa79f | 477 | $numAttachments = Civi::settings()->get('max_attachments'); |
481a74f4 | 478 | for ($i = 1; $i <= $numAttachments; $i++) { |
6a488035 | 479 | $attachName = "attachFile_$i"; |
481a74f4 | 480 | if (isset($params[$attachName]) && !empty($params[$attachName])) { |
6a488035 TO |
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. | |
a7488080 | 491 | if (!empty($params['resetTimeline']) && $referenceActivityName == 'Open Case') { |
6a488035 TO |
492 | $activityDate = $params['activity_date_time']; |
493 | } | |
494 | else { | |
9c1bc317 | 495 | $referenceActivityInfo = $activityTypes[$referenceActivityName] ?? NULL; |
6a488035 | 496 | if ($referenceActivityInfo['id']) { |
be2fb01f | 497 | $caseActivityParams = ['activity_type_id' => $referenceActivityInfo['id']]; |
6a488035 TO |
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) { | |
9c1bc317 | 508 | $activityDate = $details['activity_date'] ?? NULL; |
6a488035 TO |
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']; | |
a7488080 | 537 | if (!empty($activityParams['is_auto'])) { |
6a488035 TO |
538 | $activityParams['skipRecentView'] = TRUE; |
539 | } | |
540 | ||
d66c61b6 | 541 | // @todo - switch to using api & remove the parameter pre-wrangling above. |
6a488035 TO |
542 | $activity = CRM_Activity_BAO_Activity::create($activityParams); |
543 | ||
544 | if (!$activity) { | |
b5834543 | 545 | throw new CRM_Core_Exception('Unable to create Activity'); |
6a488035 TO |
546 | } |
547 | ||
548 | // create case activity record | |
be2fb01f | 549 | $caseParams = [ |
6a488035 TO |
550 | 'activity_id' => $activity->id, |
551 | 'case_id' => $params['caseID'], | |
be2fb01f | 552 | ]; |
6a488035 TO |
553 | CRM_Case_BAO_Case::processCaseActivity($caseParams); |
554 | return TRUE; | |
555 | } | |
556 | ||
ad8d1ce3 RO |
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', | |
f157740d | 603 | 'options' => ['limit' => 0], |
ad8d1ce3 RO |
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) { | |
68098e7b RO |
623 | $isDefaultRelationshipDefined = isset($activityTypeXML->default_assignee_relationship) |
624 | && preg_match('/\d+_[ab]_[ab]/', $activityTypeXML->default_assignee_relationship); | |
625 | ||
626 | if (!$isDefaultRelationshipDefined) { | |
ad8d1ce3 RO |
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']; | |
68098e7b | 633 | list($relTypeId, $a, $b) = explode('_', $activityTypeXML->default_assignee_relationship); |
ad8d1ce3 | 634 | |
68098e7b RO |
635 | $params = [ |
636 | 'relationship_type_id' => $relTypeId, | |
637 | "contact_id_$b" => $targetContactId, | |
ad8d1ce3 | 638 | 'is_active' => 1, |
68098e7b RO |
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); | |
ad8d1ce3 RO |
647 | |
648 | if ($relationships['count']) { | |
68098e7b RO |
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']; | |
ad8d1ce3 RO |
655 | } |
656 | else { | |
657 | return NULL; | |
658 | } | |
659 | } | |
660 | ||
68098e7b RO |
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, | |
f157740d | 670 | 'options' => ['limit' => 1], |
68098e7b RO |
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 | ||
ad8d1ce3 RO |
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', [ | |
f157740d | 696 | 'id' => $activityTypeXML->default_assignee_contact, |
ad8d1ce3 RO |
697 | ]); |
698 | ||
699 | if ($contact['count'] == 1) { | |
700 | return $activityTypeXML->default_assignee_contact; | |
701 | } | |
702 | ||
703 | return NULL; | |
704 | } | |
705 | ||
4c6ce474 EM |
706 | /** |
707 | * @param $activitySetsXML | |
708 | * | |
709 | * @return array | |
710 | */ | |
00be9182 | 711 | public static function activitySets($activitySetsXML) { |
be2fb01f | 712 | $result = []; |
6a488035 TO |
713 | foreach ($activitySetsXML as $activitySetXML) { |
714 | foreach ($activitySetXML as $recordXML) { | |
2ab37b05 | 715 | $activitySetName = (string) $recordXML->name; |
716 | $activitySetLabel = (string) $recordXML->label; | |
6a488035 TO |
717 | $result[$activitySetName] = $activitySetLabel; |
718 | } | |
719 | } | |
720 | ||
721 | return $result; | |
722 | } | |
723 | ||
4c6ce474 EM |
724 | /** |
725 | * @param $caseType | |
ac15829d | 726 | * @param string|null $activityTypeName |
4c6ce474 EM |
727 | * |
728 | * @return array|bool|mixed | |
ac15829d | 729 | * @throws CRM_Core_Exception |
4c6ce474 | 730 | */ |
00be9182 | 731 | public function getMaxInstance($caseType, $activityTypeName = NULL) { |
6a488035 TO |
732 | $xml = $this->retrieve($caseType); |
733 | ||
734 | if ($xml === FALSE) { | |
ac15829d | 735 | throw new CRM_Core_Exception('Unable to locate xml definition for case type ' . $caseType); |
6a488035 TO |
736 | } |
737 | ||
738 | $activityInstances = $this->activityTypes($xml->ActivityTypes, TRUE); | |
739 | return $activityTypeName ? CRM_Utils_Array::value($activityTypeName, $activityInstances) : $activityInstances; | |
740 | } | |
741 | ||
4c6ce474 EM |
742 | /** |
743 | * @param $caseType | |
744 | * | |
745 | * @return array|mixed | |
746 | */ | |
00be9182 | 747 | public function getCaseManagerRoleId($caseType) { |
6a488035 TO |
748 | $xml = $this->retrieve($caseType); |
749 | return $this->caseRoles($xml->CaseRoles, TRUE); | |
750 | } | |
751 | ||
708d8fa2 TO |
752 | /** |
753 | * @param string $caseType | |
ad8d1ce3 | 754 | * |
708d8fa2 TO |
755 | * @return array<\Civi\CCase\CaseChangeListener> |
756 | */ | |
00be9182 | 757 | public function getListeners($caseType) { |
708d8fa2 | 758 | $xml = $this->retrieve($caseType); |
be2fb01f | 759 | $listeners = []; |
708d8fa2 TO |
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 | ||
4c6ce474 EM |
769 | /** |
770 | * @return int | |
771 | */ | |
00be9182 | 772 | public function getRedactActivityEmail() { |
23d6731b | 773 | return $this->getBoolSetting('civicaseRedactActivityEmail', 'RedactActivityEmail'); |
6a488035 TO |
774 | } |
775 | ||
776 | /** | |
fe482240 | 777 | * Retrieves AllowMultipleCaseClients setting. |
6a488035 | 778 | * |
a6c01b45 CW |
779 | * @return string |
780 | * 1 if allowed, 0 if not | |
6a488035 | 781 | */ |
00be9182 | 782 | public function getAllowMultipleCaseClients() { |
c8fd28dc | 783 | return $this->getBoolSetting('civicaseAllowMultipleClients', 'AllowMultipleCaseClients'); |
6a488035 TO |
784 | } |
785 | ||
786 | /** | |
fe482240 | 787 | * Retrieves NaturalActivityTypeSort setting. |
6a488035 | 788 | * |
a6c01b45 CW |
789 | * @return string |
790 | * 1 if natural, 0 if alphabetic | |
6a488035 | 791 | */ |
00be9182 | 792 | public function getNaturalActivityTypeSort() { |
23d6731b TO |
793 | return $this->getBoolSetting('civicaseNaturalActivityTypeSort', 'NaturalActivityTypeSort'); |
794 | } | |
795 | ||
796 | /** | |
797 | * @param string $settingKey | |
798 | * @param string $xmlTag | |
799 | * @param mixed $default | |
ad8d1ce3 | 800 | * |
23d6731b TO |
801 | * @return int |
802 | */ | |
803 | private function getBoolSetting($settingKey, $xmlTag, $default = 0) { | |
804 | $setting = Civi::settings()->get($settingKey); | |
ec61a2b2 | 805 | if ($setting !== 'default') { |
d6742e0f TO |
806 | return (int) $setting; |
807 | } | |
23d6731b TO |
808 | if ($xml = $this->retrieve("Settings")) { |
809 | return (string) $xml->{$xmlTag} ? 1 : 0; | |
810 | } | |
811 | return $default; | |
6a488035 | 812 | } |
96025800 | 813 | |
90b5313b D |
814 | /** |
815 | * At some point name and label got mixed up for case roles. | |
2058bf54 D |
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. | |
90b5313b D |
822 | * |
823 | * @param SimpleXMLElement $xml | |
824 | * | |
2058bf54 | 825 | * @return array[bool|string,string] |
90b5313b D |
826 | */ |
827 | public function locateNameOrLabel($xml) { | |
2058bf54 D |
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 | ]; | |
90b5313b | 865 | } |
2058bf54 D |
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]; | |
90b5313b D |
877 | } |
878 | ||
6a488035 | 879 | } |