Commit | Line | Data |
---|---|---|
8ffdec17 ARW |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
bc77d7c0 | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
8ffdec17 | 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 | | |
8ffdec17 | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
8ffdec17 ARW |
11 | |
12 | /** | |
13 | * | |
14 | * @package CRM | |
ca5cec67 | 15 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
8ffdec17 ARW |
16 | */ |
17 | ||
18 | /** | |
67d19299 | 19 | * This class contains the functions for Case Type management. |
8ffdec17 ARW |
20 | */ |
21 | class CRM_Case_BAO_CaseType extends CRM_Case_DAO_CaseType { | |
22 | ||
23 | /** | |
d2e5d2ce | 24 | * Static field for all the case information that we can potentially export. |
8ffdec17 ARW |
25 | * |
26 | * @var array | |
8ffdec17 | 27 | */ |
f157740d | 28 | public static $_exportableFields = NULL; |
8ffdec17 ARW |
29 | |
30 | /** | |
d2e5d2ce | 31 | * Takes an associative array and creates a Case Type object. |
8ffdec17 ARW |
32 | * |
33 | * the function extract all the params it needs to initialize the create a | |
34 | * case type object. the params array could contain additional unused name/value | |
35 | * pairs | |
36 | * | |
64bd5a0e TO |
37 | * @param array $params |
38 | * (reference ) an assoc array of name/value pairs. | |
77b97be7 | 39 | * |
c490a46a | 40 | * @throws CRM_Core_Exception |
8ffdec17 | 41 | * |
16b10e64 | 42 | * @return CRM_Case_BAO_CaseType |
8ffdec17 | 43 | */ |
00be9182 | 44 | public static function add(&$params) { |
8ffdec17 | 45 | $caseTypeDAO = new CRM_Case_DAO_CaseType(); |
20173e0e | 46 | |
c7bccb5f | 47 | // form the name only if missing: CRM-627 |
48 | $nameParam = CRM_Utils_Array::value('name', $params, NULL); | |
49 | if (!$nameParam && empty($params['id'])) { | |
50 | $params['name'] = CRM_Utils_String::titleToVar($params['title']); | |
51 | } | |
46ec593d TO |
52 | |
53 | // Old case-types (pre-4.5) may keep their ucky names, but new case-types must satisfy isValidName() | |
54 | if (empty($params['id']) && !empty($params['name']) && !CRM_Case_BAO_CaseType::isValidName($params['name'])) { | |
55 | throw new CRM_Core_Exception("Cannot create new case-type with malformed name [{$params['name']}]"); | |
076f81b6 | 56 | } |
c7bccb5f | 57 | |
32f1c917 TO |
58 | $caseTypeName = (isset($params['name'])) ? $params['name'] : CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $params['id'], 'name', 'id', TRUE); |
59 | ||
20173e0e | 60 | // function to format definition column |
c2f2bbe6 | 61 | if (isset($params['definition']) && is_array($params['definition'])) { |
32f1c917 | 62 | $params['definition'] = self::convertDefinitionToXML($caseTypeName, $params['definition']); |
ba3228d1 | 63 | CRM_Core_ManagedEntities::scheduleReconciliation(); |
c2f2bbe6 | 64 | } |
20173e0e | 65 | |
88396939 | 66 | $caseTypeDAO->copyValues($params); |
e492e278 TO |
67 | $result = $caseTypeDAO->save(); |
68 | CRM_Case_XMLRepository::singleton()->flush(); | |
69 | return $result; | |
8ffdec17 ARW |
70 | } |
71 | ||
cd5823ae EM |
72 | /** |
73 | * Generate and assign an arbitrary value to a field of a test object. | |
74 | * | |
75 | * @param string $fieldName | |
76 | * @param array $fieldDef | |
77 | * @param int $counter | |
78 | * The globally-unique ID of the test object. | |
79 | */ | |
1753bd71 | 80 | protected function assignTestValue($fieldName, &$fieldDef, $counter) { |
e547f744 | 81 | if ($fieldName == 'definition') { |
1753bd71 | 82 | $this->{$fieldName} = "<CaseType><name>TestCaseType{$counter}</name></CaseType>"; |
0db6c3e1 TO |
83 | } |
84 | else { | |
1753bd71 TO |
85 | parent::assignTestValue($fieldName, $fieldDef, $counter); |
86 | } | |
87 | } | |
88 | ||
20173e0e | 89 | /** |
100fef9d | 90 | * Format / convert submitted array to xml for case type definition |
20173e0e | 91 | * |
10b0bbee | 92 | * @param string $name |
64bd5a0e | 93 | * @param array $definition |
b44e3f84 | 94 | * The case-type definition expressed as an array-tree. |
a6c01b45 CW |
95 | * @return string |
96 | * XML | |
20173e0e | 97 | */ |
00be9182 | 98 | public static function convertDefinitionToXML($name, $definition) { |
c9c175f9 | 99 | $xmlFile = '<?xml version="1.0" encoding="utf-8" ?>' . "\n\n<CaseType>\n"; |
e0d34d59 | 100 | $xmlFile .= "<name>" . self::encodeXmlString($name) . "</name>\n"; |
20173e0e | 101 | |
466fba66 TO |
102 | if (array_key_exists('forkable', $definition)) { |
103 | $xmlFile .= "<forkable>" . ((int) $definition['forkable']) . "</forkable>\n"; | |
104 | } | |
105 | ||
728b5029 | 106 | if (isset($definition['activityTypes'])) { |
20173e0e | 107 | $xmlFile .= "<ActivityTypes>\n"; |
10b0bbee | 108 | foreach ($definition['activityTypes'] as $values) { |
20173e0e | 109 | $xmlFile .= "<ActivityType>\n"; |
110 | foreach ($values as $key => $value) { | |
e0d34d59 | 111 | $xmlFile .= "<{$key}>" . self::encodeXmlString($value) . "</{$key}>\n"; |
20173e0e | 112 | } |
113 | $xmlFile .= "</ActivityType>\n"; | |
114 | } | |
115 | $xmlFile .= "</ActivityTypes>\n"; | |
116 | } | |
117 | ||
7c2b40d1 CW |
118 | if (!empty($definition['statuses'])) { |
119 | $xmlFile .= "<Statuses>\n"; | |
120 | foreach ($definition['statuses'] as $value) { | |
121 | $xmlFile .= "<Status>$value</Status>\n"; | |
122 | } | |
123 | $xmlFile .= "</Statuses>\n"; | |
124 | } | |
125 | ||
728b5029 | 126 | if (isset($definition['activitySets'])) { |
20173e0e | 127 | $xmlFile .= "<ActivitySets>\n"; |
10b0bbee | 128 | foreach ($definition['activitySets'] as $k => $val) { |
834bc8e2 | 129 | $xmlFile .= "<ActivitySet>\n"; |
130 | foreach ($val as $index => $setVal) { | |
2f58fb09 TO |
131 | switch ($index) { |
132 | case 'activityTypes': | |
133 | if (!empty($setVal)) { | |
134 | $xmlFile .= "<ActivityTypes>\n"; | |
135 | foreach ($setVal as $values) { | |
136 | $xmlFile .= "<ActivityType>\n"; | |
137 | foreach ($values as $key => $value) { | |
e0d34d59 | 138 | $xmlFile .= "<{$key}>" . self::encodeXmlString($value) . "</{$key}>\n"; |
2f58fb09 TO |
139 | } |
140 | $xmlFile .= "</ActivityType>\n"; | |
834bc8e2 | 141 | } |
2f58fb09 | 142 | $xmlFile .= "</ActivityTypes>\n"; |
20173e0e | 143 | } |
2f58fb09 | 144 | break; |
e547f744 | 145 | |
5d4fcf54 TO |
146 | // passthrough |
147 | case 'sequence': | |
2f58fb09 TO |
148 | case 'timeline': |
149 | if ($setVal) { | |
150 | $xmlFile .= "<{$index}>true</{$index}>\n"; | |
151 | } | |
152 | break; | |
e547f744 | 153 | |
2f58fb09 | 154 | default: |
e0d34d59 | 155 | $xmlFile .= "<{$index}>" . self::encodeXmlString($setVal) . "</{$index}>\n"; |
20173e0e | 156 | } |
157 | } | |
834bc8e2 | 158 | |
159 | $xmlFile .= "</ActivitySet>\n"; | |
20173e0e | 160 | } |
161 | ||
20173e0e | 162 | $xmlFile .= "</ActivitySets>\n"; |
163 | } | |
164 | ||
728b5029 | 165 | if (isset($definition['caseRoles'])) { |
20173e0e | 166 | $xmlFile .= "<CaseRoles>\n"; |
10b0bbee | 167 | foreach ($definition['caseRoles'] as $values) { |
20173e0e | 168 | $xmlFile .= "<RelationshipType>\n"; |
169 | foreach ($values as $key => $value) { | |
c4bfbde3 | 170 | $xmlFile .= "<{$key}>" . ($key == 'groups' ? implode(',', array_map(['\CRM_Case_BAO_CaseType', 'encodeXmlString'], (array) $value)) : self::encodeXmlString($value)) . "</{$key}>\n"; |
20173e0e | 171 | } |
172 | $xmlFile .= "</RelationshipType>\n"; | |
173 | } | |
174 | $xmlFile .= "</CaseRoles>\n"; | |
175 | } | |
176 | ||
06f21064 TO |
177 | if (array_key_exists('restrictActivityAsgmtToCmsUser', $definition)) { |
178 | $xmlFile .= "<RestrictActivityAsgmtToCmsUser>" . $definition['restrictActivityAsgmtToCmsUser'] . "</RestrictActivityAsgmtToCmsUser>\n"; | |
179 | } | |
180 | ||
181 | if (!empty($definition['activityAsgmtGrps'])) { | |
182 | $xmlFile .= "<ActivityAsgmtGrps>\n"; | |
c4bfbde3 | 183 | foreach ((array) $definition['activityAsgmtGrps'] as $value) { |
06f21064 TO |
184 | $xmlFile .= "<Group>$value</Group>\n"; |
185 | } | |
186 | $xmlFile .= "</ActivityAsgmtGrps>\n"; | |
187 | } | |
188 | ||
20173e0e | 189 | $xmlFile .= '</CaseType>'; |
06f21064 | 190 | |
10b0bbee | 191 | return $xmlFile; |
20173e0e | 192 | } |
193 | ||
e0d34d59 TO |
194 | /** |
195 | * Ugh. This shouldn't exist. Use a real XML-encoder. | |
196 | * | |
197 | * Escape a string for use in XML. | |
198 | * | |
199 | * @param string $str | |
200 | * A string which should outputted to XML. | |
201 | * @return string | |
202 | * @deprecated | |
203 | */ | |
204 | protected static function encodeXmlString($str) { | |
205 | // PHP 5.4: return htmlspecialchars($str, ENT_XML1, 'UTF-8') | |
b7172dd6 D |
206 | if (is_scalar($str)) { |
207 | return htmlspecialchars($str); | |
208 | } | |
209 | else { | |
210 | return NULL; | |
211 | } | |
e0d34d59 TO |
212 | } |
213 | ||
20173e0e | 214 | /** |
d2e5d2ce | 215 | * Get the case definition either from db or read from xml file. |
20173e0e | 216 | * |
64bd5a0e TO |
217 | * @param SimpleXmlElement $xml |
218 | * A single case-type record. | |
20173e0e | 219 | * |
a6c01b45 CW |
220 | * @return array |
221 | * the definition of the case-type, expressed as PHP array-tree | |
20173e0e | 222 | */ |
00be9182 | 223 | public static function convertXmlToDefinition($xml) { |
93d47462 | 224 | // build PHP array based on definition |
be2fb01f | 225 | $definition = []; |
20173e0e | 226 | |
466fba66 TO |
227 | if (isset($xml->forkable)) { |
228 | $definition['forkable'] = (int) $xml->forkable; | |
229 | } | |
230 | ||
06f21064 TO |
231 | if (isset($xml->RestrictActivityAsgmtToCmsUser)) { |
232 | $definition['restrictActivityAsgmtToCmsUser'] = (int) $xml->RestrictActivityAsgmtToCmsUser; | |
233 | } | |
234 | ||
235 | if (isset($xml->ActivityAsgmtGrps)) { | |
236 | $definition['activityAsgmtGrps'] = (array) $xml->ActivityAsgmtGrps->Group; | |
85148081 CW |
237 | // Backwards compat - convert group ids to group names if ids are supplied |
238 | if (array_filter($definition['activityAsgmtGrps'], ['\CRM_Utils_Rule', 'integer']) === $definition['activityAsgmtGrps']) { | |
239 | foreach ($definition['activityAsgmtGrps'] as $idx => $group) { | |
240 | $definition['activityAsgmtGrps'][$idx] = CRM_Core_DAO::getFieldValue('CRM_Contact_BAO_Group', $group); | |
241 | } | |
242 | } | |
06f21064 TO |
243 | } |
244 | ||
20173e0e | 245 | // set activity types |
728b5029 | 246 | if (isset($xml->ActivityTypes)) { |
be2fb01f | 247 | $definition['activityTypes'] = []; |
728b5029 TO |
248 | foreach ($xml->ActivityTypes->ActivityType as $activityTypeXML) { |
249 | $definition['activityTypes'][] = json_decode(json_encode($activityTypeXML), TRUE); | |
250 | } | |
ae195e71 | 251 | } |
252 | ||
7c2b40d1 CW |
253 | // set statuses |
254 | if (isset($xml->Statuses)) { | |
255 | $definition['statuses'] = (array) $xml->Statuses->Status; | |
256 | } | |
257 | ||
728b5029 TO |
258 | // set activity sets |
259 | if (isset($xml->ActivitySets)) { | |
be2fb01f CW |
260 | $definition['activitySets'] = []; |
261 | $definition['timelineActivityTypes'] = []; | |
093f1cfd | 262 | |
728b5029 TO |
263 | foreach ($xml->ActivitySets->ActivitySet as $activitySetXML) { |
264 | // parse basic properties | |
be2fb01f | 265 | $activitySet = []; |
2f58fb09 TO |
266 | $activitySet['name'] = (string) $activitySetXML->name; |
267 | $activitySet['label'] = (string) $activitySetXML->label; | |
268 | if ('true' == (string) $activitySetXML->timeline) { | |
269 | $activitySet['timeline'] = 1; | |
270 | } | |
271 | if ('true' == (string) $activitySetXML->sequence) { | |
272 | $activitySet['sequence'] = 1; | |
273 | } | |
728b5029 | 274 | |
728b5029 | 275 | if (isset($activitySetXML->ActivityTypes)) { |
be2fb01f | 276 | $activitySet['activityTypes'] = []; |
728b5029 | 277 | foreach ($activitySetXML->ActivityTypes->ActivityType as $activityTypeXML) { |
093f1cfd AP |
278 | $activityType = json_decode(json_encode($activityTypeXML), TRUE); |
279 | $activitySet['activityTypes'][] = $activityType; | |
280 | if ($activitySetXML->timeline) { | |
281 | $definition['timelineActivityTypes'][] = $activityType; | |
282 | } | |
728b5029 | 283 | } |
ae195e71 | 284 | } |
728b5029 | 285 | $definition['activitySets'][] = $activitySet; |
ae195e71 | 286 | } |
b6e48667 | 287 | } |
20173e0e | 288 | |
289 | // set case roles | |
728b5029 | 290 | if (isset($xml->CaseRoles)) { |
be2fb01f | 291 | $definition['caseRoles'] = []; |
728b5029 | 292 | foreach ($xml->CaseRoles->RelationshipType as $caseRoleXml) { |
9c7ffe36 CW |
293 | $caseRole = json_decode(json_encode($caseRoleXml), TRUE); |
294 | if (!empty($caseRole['groups'])) { | |
295 | $caseRole['groups'] = explode(',', $caseRole['groups']); | |
296 | } | |
297 | $definition['caseRoles'][] = $caseRole; | |
728b5029 TO |
298 | } |
299 | } | |
20173e0e | 300 | |
93d47462 | 301 | return $definition; |
20173e0e | 302 | } |
303 | ||
8ffdec17 ARW |
304 | /** |
305 | * Given the list of params in the params array, fetch the object | |
306 | * and store the values in the values array | |
307 | * | |
64bd5a0e TO |
308 | * @param array $params |
309 | * Input parameters to find object. | |
310 | * @param array $values | |
311 | * Output values of the object. | |
77b97be7 | 312 | * |
8ffdec17 | 313 | * @return CRM_Case_BAO_CaseType|null the found object or null |
8ffdec17 | 314 | */ |
00be9182 | 315 | public static function &getValues(&$params, &$values) { |
8ffdec17 ARW |
316 | $caseType = new CRM_Case_BAO_CaseType(); |
317 | ||
318 | $caseType->copyValues($params); | |
319 | ||
320 | if ($caseType->find(TRUE)) { | |
321 | CRM_Core_DAO::storeValues($caseType, $values); | |
322 | return $caseType; | |
323 | } | |
324 | return NULL; | |
325 | } | |
326 | ||
327 | /** | |
d2e5d2ce | 328 | * Takes an associative array and creates a case type object. |
8ffdec17 | 329 | * |
64bd5a0e TO |
330 | * @param array $params |
331 | * (reference ) an assoc array of name/value pairs. | |
77b97be7 | 332 | * |
16b10e64 | 333 | * @return CRM_Case_BAO_CaseType |
8ffdec17 | 334 | */ |
00be9182 | 335 | public static function &create(&$params) { |
8ffdec17 ARW |
336 | $transaction = new CRM_Core_Transaction(); |
337 | ||
338 | if (!empty($params['id'])) { | |
339 | CRM_Utils_Hook::pre('edit', 'CaseType', $params['id'], $params); | |
340 | } | |
341 | else { | |
342 | CRM_Utils_Hook::pre('create', 'CaseType', NULL, $params); | |
343 | } | |
344 | ||
345 | $caseType = self::add($params); | |
346 | ||
347 | if (is_a($caseType, 'CRM_Core_Error')) { | |
348 | $transaction->rollback(); | |
349 | return $caseType; | |
350 | } | |
351 | ||
352 | if (!empty($params['id'])) { | |
353 | CRM_Utils_Hook::post('edit', 'CaseType', $caseType->id, $case); | |
354 | } | |
355 | else { | |
356 | CRM_Utils_Hook::post('create', 'CaseType', $caseType->id, $case); | |
357 | } | |
358 | $transaction->commit(); | |
10ffff26 | 359 | CRM_Case_XMLRepository::singleton(TRUE); |
31c28ed5 | 360 | CRM_Core_OptionGroup::flushAll(); |
8ffdec17 ARW |
361 | |
362 | return $caseType; | |
363 | } | |
364 | ||
365 | /** | |
fe482240 EM |
366 | * Retrieve DB object based on input parameters. |
367 | * | |
368 | * It also stores all the retrieved values in the default array. | |
8ffdec17 | 369 | * |
64bd5a0e TO |
370 | * @param array $params |
371 | * (reference ) an assoc array of name/value pairs. | |
372 | * @param array $defaults | |
373 | * (reference ) an assoc array to hold the name / value pairs. | |
8ffdec17 | 374 | * in a hierarchical manner |
da6b46f4 | 375 | * |
16b10e64 | 376 | * @return CRM_Case_BAO_CaseType |
8ffdec17 | 377 | */ |
00be9182 | 378 | public static function retrieve(&$params, &$defaults) { |
8ffdec17 ARW |
379 | $caseType = CRM_Case_BAO_CaseType::getValues($params, $defaults); |
380 | return $caseType; | |
381 | } | |
382 | ||
4c6ce474 | 383 | /** |
100fef9d | 384 | * @param int $caseTypeId |
4c6ce474 | 385 | * |
c490a46a | 386 | * @throws CRM_Core_Exception |
4c6ce474 EM |
387 | * @return mixed |
388 | */ | |
00be9182 | 389 | public static function del($caseTypeId) { |
8ffdec17 ARW |
390 | $caseType = new CRM_Case_DAO_CaseType(); |
391 | $caseType->id = $caseTypeId; | |
10ffff26 TO |
392 | $refCounts = $caseType->getReferenceCounts(); |
393 | $total = array_sum(CRM_Utils_Array::collect('count', $refCounts)); | |
394 | if ($total) { | |
be2fb01f | 395 | throw new CRM_Core_Exception(ts("You can not delete this case type -- it is assigned to %1 existing case record(s). If you do not want this case type to be used going forward, consider disabling it instead.", [1 => $total])); |
10ffff26 TO |
396 | } |
397 | $result = $caseType->delete(); | |
398 | CRM_Case_XMLRepository::singleton(TRUE); | |
399 | return $result; | |
8ffdec17 | 400 | } |
076f81b6 | 401 | |
402 | /** | |
403 | * Determine if a case-type name is well-formed | |
404 | * | |
405 | * @param string $caseType | |
406 | * @return bool | |
407 | */ | |
00be9182 | 408 | public static function isValidName($caseType) { |
e547f744 | 409 | return preg_match('/^[a-zA-Z0-9_]+$/', $caseType); |
076f81b6 | 410 | } |
32f1c917 TO |
411 | |
412 | /** | |
413 | * Determine if the case-type has *both* DB and file-based definitions. | |
414 | * | |
415 | * @param int $caseTypeId | |
72b3a70c CW |
416 | * @return bool|null |
417 | * TRUE if there are *both* DB and file-based definitions | |
32f1c917 | 418 | */ |
00be9182 | 419 | public static function isForked($caseTypeId) { |
32f1c917 TO |
420 | $caseTypeName = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'name', 'id', TRUE); |
421 | if ($caseTypeName) { | |
422 | $dbDefinition = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'definition', 'id', TRUE); | |
423 | $fileDefinition = CRM_Case_XMLRepository::singleton()->retrieveFile($caseTypeName); | |
424 | return $fileDefinition && $dbDefinition; | |
425 | } | |
426 | return NULL; | |
427 | } | |
428 | ||
429 | /** | |
430 | * Determine if modifications are allowed on the case-type | |
431 | * | |
432 | * @param int $caseTypeId | |
a6c01b45 CW |
433 | * @return bool |
434 | * TRUE if the definition can be modified | |
32f1c917 | 435 | */ |
00be9182 | 436 | public static function isForkable($caseTypeId) { |
32f1c917 TO |
437 | $caseTypeName = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'name', 'id', TRUE); |
438 | if ($caseTypeName) { | |
439 | // if file-based definition explicitly disables "forkable" option, then don't allow changes to definition | |
440 | $fileDefinition = CRM_Case_XMLRepository::singleton()->retrieveFile($caseTypeName); | |
2210eab6 | 441 | if ($fileDefinition && isset($fileDefinition->forkable)) { |
e547f744 | 442 | return CRM_Utils_String::strtobool((string) $fileDefinition->forkable); |
32f1c917 TO |
443 | } |
444 | } | |
445 | return TRUE; | |
446 | } | |
96025800 | 447 | |
8ffdec17 | 448 | } |