Commit | Line | Data |
---|---|---|
7f254ad8 AE |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
4 | | CiviCRM version 4.6 | | |
5 | +--------------------------------------------------------------------+ | |
6 | | Copyright CiviCRM LLC (c) 2004-2015 | | |
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 | +--------------------------------------------------------------------+ | |
26 | */ | |
27 | ||
28 | /** | |
29 | * | |
30 | * @package CRM | |
31 | * @copyright CiviCRM LLC (c) 2004-2015 | |
32 | * $Id$ | |
33 | * | |
34 | */ | |
35 | ||
36 | /** | |
37 | * This class contains the functions for Case Type management | |
38 | * | |
39 | */ | |
40 | class CRM_Case_BAO_CaseType extends CRM_Case_DAO_CaseType { | |
41 | ||
42 | /** | |
43 | * Static field for all the case information that we can potentially export. | |
44 | * | |
45 | * @var array | |
46 | */ | |
47 | static $_exportableFields = NULL; | |
48 | ||
49 | /** | |
50 | * Takes an associative array and creates a Case Type object. | |
51 | * | |
52 | * the function extract all the params it needs to initialize the create a | |
53 | * case type object. the params array could contain additional unused name/value | |
54 | * pairs | |
55 | * | |
56 | * @param array $params | |
57 | * (reference ) an assoc array of name/value pairs. | |
58 | * | |
59 | * @throws CRM_Core_Exception | |
60 | * | |
61 | * @return CRM_Case_BAO_CaseType | |
62 | */ | |
63 | public static function add(&$params) { | |
64 | $caseTypeDAO = new CRM_Case_DAO_CaseType(); | |
65 | ||
66 | // form the name only if missing: CRM-627 | |
67 | $nameParam = CRM_Utils_Array::value('name', $params, NULL); | |
68 | if (!$nameParam && empty($params['id'])) { | |
69 | $params['name'] = CRM_Utils_String::titleToVar($params['title']); | |
70 | } | |
71 | ||
72 | // Old case-types (pre-4.5) may keep their ucky names, but new case-types must satisfy isValidName() | |
73 | if (empty($params['id']) && !empty($params['name']) && !CRM_Case_BAO_CaseType::isValidName($params['name'])) { | |
74 | throw new CRM_Core_Exception("Cannot create new case-type with malformed name [{$params['name']}]"); | |
75 | } | |
76 | ||
77 | $caseTypeName = (isset($params['name'])) ? $params['name'] : CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $params['id'], 'name', 'id', TRUE); | |
78 | ||
79 | // function to format definition column | |
80 | if (isset($params['definition']) && is_array($params['definition'])) { | |
81 | $params['definition'] = self::convertDefinitionToXML($caseTypeName, $params['definition']); | |
82 | CRM_Core_ManagedEntities::scheduleReconciliation(); | |
83 | } | |
84 | ||
85 | $caseTypeDAO->copyValues($params); | |
86 | return $caseTypeDAO->save(); | |
87 | } | |
88 | ||
89 | /** | |
90 | * Generate and assign an arbitrary value to a field of a test object. | |
91 | * | |
92 | * @param string $fieldName | |
93 | * @param array $fieldDef | |
94 | * @param int $counter | |
95 | * The globally-unique ID of the test object. | |
96 | */ | |
97 | protected function assignTestValue($fieldName, &$fieldDef, $counter) { | |
98 | if ($fieldName == 'definition') { | |
99 | $this->{$fieldName} = "<CaseType><name>TestCaseType{$counter}</name></CaseType>"; | |
100 | } | |
101 | else { | |
102 | parent::assignTestValue($fieldName, $fieldDef, $counter); | |
103 | } | |
104 | } | |
105 | ||
106 | ||
107 | /** | |
108 | * Format / convert submitted array to xml for case type definition | |
109 | * | |
110 | * @param string $name | |
111 | * @param array $definition | |
112 | * The case-type defintion expressed as an array-tree. | |
113 | * @return string | |
114 | * XML | |
115 | */ | |
116 | public static function convertDefinitionToXML($name, $definition) { | |
117 | $xmlFile = '<?xml version="1.0" encoding="utf-8" ?>' . "\n\n<CaseType>\n"; | |
118 | $xmlFile .= "<name>" . self::encodeXmlString($name) . "</name>\n"; | |
119 | ||
120 | if (array_key_exists('forkable', $definition)) { | |
121 | $xmlFile .= "<forkable>" . ((int) $definition['forkable']) . "</forkable>\n"; | |
122 | } | |
123 | ||
124 | if (isset($definition['activityTypes'])) { | |
125 | $xmlFile .= "<ActivityTypes>\n"; | |
126 | foreach ($definition['activityTypes'] as $values) { | |
127 | $xmlFile .= "<ActivityType>\n"; | |
128 | foreach ($values as $key => $value) { | |
129 | $xmlFile .= "<{$key}>" . self::encodeXmlString($value) . "</{$key}>\n"; | |
130 | } | |
131 | $xmlFile .= "</ActivityType>\n"; | |
132 | } | |
133 | $xmlFile .= "</ActivityTypes>\n"; | |
134 | } | |
135 | ||
136 | if (isset($definition['activitySets'])) { | |
137 | $xmlFile .= "<ActivitySets>\n"; | |
138 | foreach ($definition['activitySets'] as $k => $val) { | |
139 | $xmlFile .= "<ActivitySet>\n"; | |
140 | foreach ($val as $index => $setVal) { | |
141 | switch ($index) { | |
142 | case 'activityTypes': | |
143 | if (!empty($setVal)) { | |
144 | $xmlFile .= "<ActivityTypes>\n"; | |
145 | foreach ($setVal as $values) { | |
146 | $xmlFile .= "<ActivityType>\n"; | |
147 | foreach ($values as $key => $value) { | |
148 | $xmlFile .= "<{$key}>" . self::encodeXmlString($value) . "</{$key}>\n"; | |
149 | } | |
150 | $xmlFile .= "</ActivityType>\n"; | |
151 | } | |
152 | $xmlFile .= "</ActivityTypes>\n"; | |
153 | } | |
154 | break; | |
155 | ||
156 | case 'sequence': // passthrough | |
157 | case 'timeline': | |
158 | if ($setVal) { | |
159 | $xmlFile .= "<{$index}>true</{$index}>\n"; | |
160 | } | |
161 | break; | |
162 | ||
163 | default: | |
164 | $xmlFile .= "<{$index}>" . self::encodeXmlString($setVal) . "</{$index}>\n"; | |
165 | } | |
166 | } | |
167 | ||
168 | $xmlFile .= "</ActivitySet>\n"; | |
169 | } | |
170 | ||
171 | $xmlFile .= "</ActivitySets>\n"; | |
172 | } | |
173 | ||
174 | if (isset($definition['caseRoles'])) { | |
175 | $xmlFile .= "<CaseRoles>\n"; | |
176 | foreach ($definition['caseRoles'] as $values) { | |
177 | $xmlFile .= "<RelationshipType>\n"; | |
178 | foreach ($values as $key => $value) { | |
179 | $xmlFile .= "<{$key}>" . self::encodeXmlString($value) . "</{$key}>\n"; | |
180 | } | |
181 | $xmlFile .= "</RelationshipType>\n"; | |
182 | } | |
183 | $xmlFile .= "</CaseRoles>\n"; | |
184 | } | |
185 | ||
186 | $xmlFile .= '</CaseType>'; | |
187 | return $xmlFile; | |
188 | } | |
189 | ||
190 | /** | |
191 | * Ugh. This shouldn't exist. Use a real XML-encoder. | |
192 | * | |
193 | * Escape a string for use in XML. | |
194 | * | |
195 | * @param string $str | |
196 | * A string which should outputted to XML. | |
197 | * @return string | |
198 | * @deprecated | |
199 | */ | |
200 | protected static function encodeXmlString($str) { | |
201 | // PHP 5.4: return htmlspecialchars($str, ENT_XML1, 'UTF-8') | |
202 | return htmlspecialchars($str); | |
203 | } | |
204 | ||
205 | /** | |
206 | * Get the case definition either from db or read from xml file. | |
207 | * | |
208 | * @param SimpleXmlElement $xml | |
209 | * A single case-type record. | |
210 | * | |
211 | * @return array | |
212 | * the definition of the case-type, expressed as PHP array-tree | |
213 | */ | |
214 | public static function convertXmlToDefinition($xml) { | |
215 | // build PHP array based on definition | |
216 | $definition = array(); | |
217 | ||
218 | if (isset($xml->forkable)) { | |
219 | $definition['forkable'] = (int) $xml->forkable; | |
220 | } | |
221 | ||
222 | // set activity types | |
223 | if (isset($xml->ActivityTypes)) { | |
224 | $definition['activityTypes'] = array(); | |
225 | foreach ($xml->ActivityTypes->ActivityType as $activityTypeXML) { | |
226 | $definition['activityTypes'][] = json_decode(json_encode($activityTypeXML), TRUE); | |
227 | } | |
228 | } | |
229 | ||
230 | // set activity sets | |
231 | if (isset($xml->ActivitySets)) { | |
232 | $definition['activitySets'] = array(); | |
233 | foreach ($xml->ActivitySets->ActivitySet as $activitySetXML) { | |
234 | // parse basic properties | |
235 | $activitySet = array(); | |
236 | $activitySet['name'] = (string) $activitySetXML->name; | |
237 | $activitySet['label'] = (string) $activitySetXML->label; | |
238 | if ('true' == (string) $activitySetXML->timeline) { | |
239 | $activitySet['timeline'] = 1; | |
240 | } | |
241 | if ('true' == (string) $activitySetXML->sequence) { | |
242 | $activitySet['sequence'] = 1; | |
243 | } | |
244 | ||
245 | if (isset($activitySetXML->ActivityTypes)) { | |
246 | $activitySet['activityTypes'] = array(); | |
247 | foreach ($activitySetXML->ActivityTypes->ActivityType as $activityTypeXML) { | |
248 | $activitySet['activityTypes'][] = json_decode(json_encode($activityTypeXML), TRUE); | |
249 | } | |
250 | } | |
251 | $definition['activitySets'][] = $activitySet; | |
252 | } | |
253 | } | |
254 | ||
255 | // set case roles | |
256 | if (isset($xml->CaseRoles)) { | |
257 | $definition['caseRoles'] = array(); | |
258 | foreach ($xml->CaseRoles->RelationshipType as $caseRoleXml) { | |
259 | $definition['caseRoles'][] = json_decode(json_encode($caseRoleXml), TRUE); | |
260 | } | |
261 | } | |
262 | ||
263 | return $definition; | |
264 | } | |
265 | ||
266 | /** | |
267 | * Given the list of params in the params array, fetch the object | |
268 | * and store the values in the values array | |
269 | * | |
270 | * @param array $params | |
271 | * Input parameters to find object. | |
272 | * @param array $values | |
273 | * Output values of the object. | |
274 | * | |
275 | * @return CRM_Case_BAO_CaseType|null the found object or null | |
276 | */ | |
277 | public static function &getValues(&$params, &$values) { | |
278 | $caseType = new CRM_Case_BAO_CaseType(); | |
279 | ||
280 | $caseType->copyValues($params); | |
281 | ||
282 | if ($caseType->find(TRUE)) { | |
283 | CRM_Core_DAO::storeValues($caseType, $values); | |
284 | return $caseType; | |
285 | } | |
286 | return NULL; | |
287 | } | |
288 | ||
289 | /** | |
290 | * Takes an associative array and creates a case type object. | |
291 | * | |
292 | * @param array $params | |
293 | * (reference ) an assoc array of name/value pairs. | |
294 | * | |
295 | * @return CRM_Case_BAO_CaseType | |
296 | */ | |
297 | public static function &create(&$params) { | |
298 | $transaction = new CRM_Core_Transaction(); | |
299 | ||
300 | if (!empty($params['id'])) { | |
301 | CRM_Utils_Hook::pre('edit', 'CaseType', $params['id'], $params); | |
302 | } | |
303 | else { | |
304 | CRM_Utils_Hook::pre('create', 'CaseType', NULL, $params); | |
305 | } | |
306 | ||
307 | $caseType = self::add($params); | |
308 | ||
309 | if (is_a($caseType, 'CRM_Core_Error')) { | |
310 | $transaction->rollback(); | |
311 | return $caseType; | |
312 | } | |
313 | ||
314 | if (!empty($params['id'])) { | |
315 | CRM_Utils_Hook::post('edit', 'CaseType', $caseType->id, $case); | |
316 | } | |
317 | else { | |
318 | CRM_Utils_Hook::post('create', 'CaseType', $caseType->id, $case); | |
319 | } | |
320 | $transaction->commit(); | |
321 | CRM_Case_XMLRepository::singleton(TRUE); | |
322 | ||
323 | return $caseType; | |
324 | } | |
325 | ||
326 | /** | |
327 | * Retrieve DB object based on input parameters. | |
328 | * | |
329 | * It also stores all the retrieved values in the default array. | |
330 | * | |
331 | * @param array $params | |
332 | * (reference ) an assoc array of name/value pairs. | |
333 | * @param array $defaults | |
334 | * (reference ) an assoc array to hold the name / value pairs. | |
335 | * in a hierarchical manner | |
336 | * | |
337 | * @return CRM_Case_BAO_CaseType | |
338 | */ | |
339 | public static function retrieve(&$params, &$defaults) { | |
340 | $caseType = CRM_Case_BAO_CaseType::getValues($params, $defaults); | |
341 | return $caseType; | |
342 | } | |
343 | ||
344 | /** | |
345 | * @param int $caseTypeId | |
346 | * | |
347 | * @throws CRM_Core_Exception | |
348 | * @return mixed | |
349 | */ | |
350 | public static function del($caseTypeId) { | |
351 | $caseType = new CRM_Case_DAO_CaseType(); | |
352 | $caseType->id = $caseTypeId; | |
353 | $refCounts = $caseType->getReferenceCounts(); | |
354 | $total = array_sum(CRM_Utils_Array::collect('count', $refCounts)); | |
355 | if ($total) { | |
356 | 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.", array(1 => $total))); | |
357 | } | |
358 | $result = $caseType->delete(); | |
359 | CRM_Case_XMLRepository::singleton(TRUE); | |
360 | return $result; | |
361 | } | |
362 | ||
363 | /** | |
364 | * Determine if a case-type name is well-formed | |
365 | * | |
366 | * @param string $caseType | |
367 | * @return bool | |
368 | */ | |
369 | public static function isValidName($caseType) { | |
370 | return preg_match('/^[a-zA-Z0-9_]+$/', $caseType); | |
371 | } | |
372 | ||
373 | /** | |
374 | * Determine if the case-type has *both* DB and file-based definitions. | |
375 | * | |
376 | * @param int $caseTypeId | |
377 | * @return bool|null | |
378 | * TRUE if there are *both* DB and file-based definitions | |
379 | */ | |
380 | public static function isForked($caseTypeId) { | |
381 | $caseTypeName = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'name', 'id', TRUE); | |
382 | if ($caseTypeName) { | |
383 | $dbDefinition = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'definition', 'id', TRUE); | |
384 | $fileDefinition = CRM_Case_XMLRepository::singleton()->retrieveFile($caseTypeName); | |
385 | return $fileDefinition && $dbDefinition; | |
386 | } | |
387 | return NULL; | |
388 | } | |
389 | ||
390 | /** | |
391 | * Determine if modifications are allowed on the case-type | |
392 | * | |
393 | * @param int $caseTypeId | |
394 | * @return bool | |
395 | * TRUE if the definition can be modified | |
396 | */ | |
397 | public static function isForkable($caseTypeId) { | |
398 | $caseTypeName = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'name', 'id', TRUE); | |
399 | if ($caseTypeName) { | |
400 | // if file-based definition explicitly disables "forkable" option, then don't allow changes to definition | |
401 | $fileDefinition = CRM_Case_XMLRepository::singleton()->retrieveFile($caseTypeName); | |
402 | if ($fileDefinition && isset($fileDefinition->forkable)) { | |
403 | return CRM_Utils_String::strtobool((string) $fileDefinition->forkable); | |
404 | } | |
405 | } | |
406 | return TRUE; | |
407 | } | |
408 | ||
409 | } |