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