Merge pull request #3964 from totten/4.5-memlogtest
[civicrm-core.git] / CRM / Case / BAO / CaseType.php
CommitLineData
8ffdec17
ARW
1<?php
2/*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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-2014
32 * $Id$
33 *
34 */
35
36/**
37 * This class contains the functions for Case Type management
38 *
39 */
40class 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 * @static
47 */
48 static $_exportableFields = NULL;
49
50 /**
51 * takes an associative array and creates a Case Type object
52 *
53 * the function extract all the params it needs to initialize the create a
54 * case type object. the params array could contain additional unused name/value
55 * pairs
56 *
57 * @param array $params (reference ) an assoc array of name/value pairs
77b97be7
EM
58 *
59 * @internal param array $ids the array that holds all the db ids
8ffdec17
ARW
60 *
61 * @return object CRM_Case_BAO_CaseType object
62 * @access public
63 * @static
64 */
65 static function add(&$params) {
66 $caseTypeDAO = new CRM_Case_DAO_CaseType();
20173e0e 67
c7bccb5f 68 // form the name only if missing: CRM-627
69 $nameParam = CRM_Utils_Array::value('name', $params, NULL);
70 if (!$nameParam && empty($params['id'])) {
71 $params['name'] = CRM_Utils_String::titleToVar($params['title']);
72 }
46ec593d
TO
73
74 // Old case-types (pre-4.5) may keep their ucky names, but new case-types must satisfy isValidName()
75 if (empty($params['id']) && !empty($params['name']) && !CRM_Case_BAO_CaseType::isValidName($params['name'])) {
76 throw new CRM_Core_Exception("Cannot create new case-type with malformed name [{$params['name']}]");
076f81b6 77 }
c7bccb5f 78
32f1c917
TO
79 $caseTypeName = (isset($params['name'])) ? $params['name'] : CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $params['id'], 'name', 'id', TRUE);
80
20173e0e 81 // function to format definition column
c2f2bbe6 82 if (isset($params['definition']) && is_array($params['definition'])) {
32f1c917 83 $params['definition'] = self::convertDefinitionToXML($caseTypeName, $params['definition']);
9c19292b 84 CRM_Core_ManagedEntities::scheduleReconcilation();
c2f2bbe6 85 }
20173e0e 86
88396939 87 $caseTypeDAO->copyValues($params);
8ffdec17
ARW
88 return $caseTypeDAO->save();
89 }
90
1753bd71
TO
91 protected function assignTestValue($fieldName, &$fieldDef, $counter) {
92 if ($fieldName == 'definition') {
93 $this->{$fieldName} = "<CaseType><name>TestCaseType{$counter}</name></CaseType>";
94 } else {
95 parent::assignTestValue($fieldName, $fieldDef, $counter);
96 }
97 }
98
99
20173e0e 100 /**
101 * Function to format / convert submitted array to xml for case type definition
102 *
10b0bbee
TO
103 * @param string $name
104 * @param array $definition the case-type defintion expressed as an array-tree
105 * @return string XML
20173e0e 106 * @static
107 * @access public
108 */
10b0bbee 109 static function convertDefinitionToXML($name, $definition) {
20173e0e 110 $xmlFile = '<?xml version="1.0" encoding="iso-8859-1" ?>' . "\n\n<CaseType>\n";
10b0bbee 111 $xmlFile .= "<name>{$name}</name>\n";
20173e0e 112
466fba66
TO
113 if (array_key_exists('forkable', $definition)) {
114 $xmlFile .= "<forkable>" . ((int) $definition['forkable']) . "</forkable>\n";
115 }
116
728b5029 117 if (isset($definition['activityTypes'])) {
20173e0e 118 $xmlFile .= "<ActivityTypes>\n";
10b0bbee 119 foreach ($definition['activityTypes'] as $values) {
20173e0e 120 $xmlFile .= "<ActivityType>\n";
121 foreach ($values as $key => $value) {
122 $xmlFile .= "<{$key}>{$value}</{$key}>\n";
123 }
124 $xmlFile .= "</ActivityType>\n";
125 }
126 $xmlFile .= "</ActivityTypes>\n";
127 }
128
728b5029 129 if (isset($definition['activitySets'])) {
20173e0e 130 $xmlFile .= "<ActivitySets>\n";
10b0bbee 131 foreach ($definition['activitySets'] as $k => $val) {
834bc8e2 132 $xmlFile .= "<ActivitySet>\n";
133 foreach ($val as $index => $setVal) {
2f58fb09
TO
134 switch ($index) {
135 case 'activityTypes':
136 if (!empty($setVal)) {
137 $xmlFile .= "<ActivityTypes>\n";
138 foreach ($setVal as $values) {
139 $xmlFile .= "<ActivityType>\n";
140 foreach ($values as $key => $value) {
141 $xmlFile .= "<{$key}>{$value}</{$key}>\n";
142 }
143 $xmlFile .= "</ActivityType>\n";
834bc8e2 144 }
2f58fb09 145 $xmlFile .= "</ActivityTypes>\n";
20173e0e 146 }
2f58fb09
TO
147 break;
148 case 'sequence': // passthrough
149 case 'timeline':
150 if ($setVal) {
151 $xmlFile .= "<{$index}>true</{$index}>\n";
152 }
153 break;
154 default:
155 $xmlFile .= "<{$index}>{$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) {
170 $xmlFile .= "<{$key}>{$value}</{$key}>\n";
171 }
172 $xmlFile .= "</RelationshipType>\n";
173 }
174 $xmlFile .= "</CaseRoles>\n";
175 }
176
177 $xmlFile .= '</CaseType>';
10b0bbee 178 return $xmlFile;
20173e0e 179 }
180
181 /**
182 * Function to get the case definition either from db or read from xml file
183 *
10b0bbee 184 * @param SimpleXmlElement $xml a single case-type record
20173e0e 185 *
93d47462 186 * @return array the definition of the case-type, expressed as PHP array-tree
20173e0e 187 * @static
188 */
10b0bbee 189 static function convertXmlToDefinition($xml) {
93d47462
TO
190 // build PHP array based on definition
191 $definition = array();
20173e0e 192
466fba66
TO
193 if (isset($xml->forkable)) {
194 $definition['forkable'] = (int) $xml->forkable;
195 }
196
20173e0e 197 // set activity types
728b5029
TO
198 if (isset($xml->ActivityTypes)) {
199 $definition['activityTypes'] = array();
200 foreach ($xml->ActivityTypes->ActivityType as $activityTypeXML) {
201 $definition['activityTypes'][] = json_decode(json_encode($activityTypeXML), TRUE);
202 }
ae195e71 203 }
204
728b5029
TO
205 // set activity sets
206 if (isset($xml->ActivitySets)) {
207 $definition['activitySets'] = array();
208 foreach ($xml->ActivitySets->ActivitySet as $activitySetXML) {
209 // parse basic properties
2f58fb09
TO
210 $activitySet = array();
211 $activitySet['name'] = (string) $activitySetXML->name;
212 $activitySet['label'] = (string) $activitySetXML->label;
213 if ('true' == (string) $activitySetXML->timeline) {
214 $activitySet['timeline'] = 1;
215 }
216 if ('true' == (string) $activitySetXML->sequence) {
217 $activitySet['sequence'] = 1;
218 }
728b5029 219
728b5029
TO
220 if (isset($activitySetXML->ActivityTypes)) {
221 $activitySet['activityTypes'] = array();
222 foreach ($activitySetXML->ActivityTypes->ActivityType as $activityTypeXML) {
223 $activitySet['activityTypes'][] = json_decode(json_encode($activityTypeXML), TRUE);
224 }
ae195e71 225 }
728b5029 226 $definition['activitySets'][] = $activitySet;
ae195e71 227 }
b6e48667 228 }
20173e0e 229
230 // set case roles
728b5029
TO
231 if (isset($xml->CaseRoles)) {
232 $definition['caseRoles'] = array();
233 foreach ($xml->CaseRoles->RelationshipType as $caseRoleXml) {
234 $definition['caseRoles'][] = json_decode(json_encode($caseRoleXml), TRUE);
235 }
236 }
20173e0e 237
93d47462 238 return $definition;
20173e0e 239 }
240
8ffdec17
ARW
241 /**
242 * Given the list of params in the params array, fetch the object
243 * and store the values in the values array
244 *
245 * @param array $params input parameters to find object
246 * @param array $values output values of the object
77b97be7
EM
247 *
248 * @internal param array $ids the array that holds all the db ids
8ffdec17
ARW
249 *
250 * @return CRM_Case_BAO_CaseType|null the found object or null
251 * @access public
252 * @static
253 */
254 static function &getValues(&$params, &$values) {
255 $caseType = new CRM_Case_BAO_CaseType();
256
257 $caseType->copyValues($params);
258
259 if ($caseType->find(TRUE)) {
260 CRM_Core_DAO::storeValues($caseType, $values);
261 return $caseType;
262 }
263 return NULL;
264 }
265
266 /**
267 * takes an associative array and creates a case type object
268 *
269 * @param array $params (reference ) an assoc array of name/value pairs
77b97be7
EM
270 *
271 * @internal param array $ids the array that holds all the db ids
8ffdec17
ARW
272 *
273 * @return object CRM_Case_BAO_CaseType object
274 * @access public
275 * @static
276 */
277 static function &create(&$params) {
278 $transaction = new CRM_Core_Transaction();
279
280 if (!empty($params['id'])) {
281 CRM_Utils_Hook::pre('edit', 'CaseType', $params['id'], $params);
282 }
283 else {
284 CRM_Utils_Hook::pre('create', 'CaseType', NULL, $params);
285 }
286
287 $caseType = self::add($params);
288
289 if (is_a($caseType, 'CRM_Core_Error')) {
290 $transaction->rollback();
291 return $caseType;
292 }
293
294 if (!empty($params['id'])) {
295 CRM_Utils_Hook::post('edit', 'CaseType', $caseType->id, $case);
296 }
297 else {
298 CRM_Utils_Hook::post('create', 'CaseType', $caseType->id, $case);
299 }
300 $transaction->commit();
10ffff26 301 CRM_Case_XMLRepository::singleton(TRUE);
8ffdec17
ARW
302
303 return $caseType;
304 }
305
306 /**
307 * Takes a bunch of params that are needed to match certain criteria and
308 * retrieves the relevant objects. We'll tweak this function to be more
309 * full featured over a period of time. This is the inverse function of
310 * create. It also stores all the retrieved values in the default array
311 *
da6b46f4 312 * @param array $params (reference ) an assoc array of name/value pairs
8ffdec17
ARW
313 * @param array $defaults (reference ) an assoc array to hold the name / value pairs
314 * in a hierarchical manner
da6b46f4
EM
315 *
316 * @internal param array $ids (reference) the array that holds all the db ids
8ffdec17
ARW
317 *
318 * @return object CRM_Case_BAO_CaseType object
319 * @access public
320 * @static
321 */
322 static function retrieve(&$params, &$defaults) {
323 $caseType = CRM_Case_BAO_CaseType::getValues($params, $defaults);
324 return $caseType;
325 }
326
4c6ce474
EM
327 /**
328 * @param $caseTypeId
329 *
330 * @return mixed
331 */
8ffdec17
ARW
332 static function del($caseTypeId) {
333 $caseType = new CRM_Case_DAO_CaseType();
334 $caseType->id = $caseTypeId;
10ffff26
TO
335 $refCounts = $caseType->getReferenceCounts();
336 $total = array_sum(CRM_Utils_Array::collect('count', $refCounts));
337 if ($total) {
84dfe0f2 338 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)));
10ffff26
TO
339 }
340 $result = $caseType->delete();
341 CRM_Case_XMLRepository::singleton(TRUE);
342 return $result;
8ffdec17 343 }
076f81b6 344
345 /**
346 * Determine if a case-type name is well-formed
347 *
348 * @param string $caseType
349 * @return bool
350 */
351 static function isValidName($caseType) {
352 return preg_match('/^[a-zA-Z0-9_]+$/', $caseType);
353 }
32f1c917
TO
354
355 /**
356 * Determine if the case-type has *both* DB and file-based definitions.
357 *
358 * @param int $caseTypeId
359 * @return bool|null TRUE if there are *both* DB and file-based definitions
360 */
361 static function isForked($caseTypeId) {
362 $caseTypeName = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'name', 'id', TRUE);
363 if ($caseTypeName) {
364 $dbDefinition = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'definition', 'id', TRUE);
365 $fileDefinition = CRM_Case_XMLRepository::singleton()->retrieveFile($caseTypeName);
366 return $fileDefinition && $dbDefinition;
367 }
368 return NULL;
369 }
370
371 /**
372 * Determine if modifications are allowed on the case-type
373 *
374 * @param int $caseTypeId
375 * @return bool TRUE if the definition can be modified
376 */
377 static function isForkable($caseTypeId) {
378 $caseTypeName = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'name', 'id', TRUE);
379 if ($caseTypeName) {
380 // if file-based definition explicitly disables "forkable" option, then don't allow changes to definition
381 $fileDefinition = CRM_Case_XMLRepository::singleton()->retrieveFile($caseTypeName);
2210eab6
TO
382 if ($fileDefinition && isset($fileDefinition->forkable)) {
383 return CRM_Utils_String::strtobool((string)$fileDefinition->forkable);
32f1c917
TO
384 }
385 }
386 return TRUE;
387 }
8ffdec17 388}