| 1 | <?php |
| 2 | /* |
| 3 | +--------------------------------------------------------------------+ |
| 4 | | CiviCRM version 4.7 | |
| 5 | +--------------------------------------------------------------------+ |
| 6 | | Copyright CiviCRM LLC (c) 2004-2017 | |
| 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 | * This api exposes CiviCRM Case objects. |
| 30 | * Developed by woolman.org |
| 31 | * |
| 32 | * @package CiviCRM_APIv3 |
| 33 | */ |
| 34 | |
| 35 | |
| 36 | /** |
| 37 | * Open a new case, add client and manager roles, and standard timeline. |
| 38 | * |
| 39 | * @param array $params |
| 40 | * |
| 41 | * @code |
| 42 | * //REQUIRED for create: |
| 43 | * 'case_type_id' => int OR |
| 44 | * 'case_type' => str (provide one or the other) |
| 45 | * 'contact_id' => int // case client |
| 46 | * 'subject' => str |
| 47 | * //REQUIRED for update: |
| 48 | * 'id' => case Id |
| 49 | * |
| 50 | * //OPTIONAL |
| 51 | * 'medium_id' => int // see civicrm option values for possibilities |
| 52 | * 'creator_id' => int // case manager, default to the logged in user |
| 53 | * 'status_id' => int // defaults to 1 "ongoing" |
| 54 | * 'location' => str |
| 55 | * 'start_date' => str datestamp // defaults to: date('YmdHis') |
| 56 | * 'duration' => int // in minutes |
| 57 | * 'details' => str // html format |
| 58 | * @endcode |
| 59 | * |
| 60 | * @throws API_Exception |
| 61 | * @return array |
| 62 | * api result array |
| 63 | */ |
| 64 | function civicrm_api3_case_create($params) { |
| 65 | _civicrm_api3_case_format_params($params); |
| 66 | |
| 67 | if (empty($params['id'])) { |
| 68 | // Creating a new case, so make sure we have the necessary parameters |
| 69 | civicrm_api3_verify_mandatory($params, NULL, array( |
| 70 | 'contact_id', |
| 71 | 'subject', |
| 72 | array('case_type', 'case_type_id'), |
| 73 | ) |
| 74 | ); |
| 75 | } |
| 76 | else { |
| 77 | // Update an existing case |
| 78 | // FIXME: Some of this logic should move to the BAO object? |
| 79 | // FIXME: Should we check if case with ID actually exists? |
| 80 | if (!isset($params['case_id']) && isset($params['id'])) { |
| 81 | $params['case_id'] = $params['id']; |
| 82 | } |
| 83 | |
| 84 | if (array_key_exists('creator_id', $params)) { |
| 85 | throw new API_Exception('You cannot update creator id'); |
| 86 | } |
| 87 | |
| 88 | $mergedCaseId = $origContactIds = array(); |
| 89 | |
| 90 | // get original contact id and creator id of case |
| 91 | if (!empty($params['contact_id'])) { |
| 92 | $origContactIds = CRM_Case_BAO_Case::retrieveContactIdsByCaseId($params['id']); |
| 93 | $origContactId = CRM_Utils_Array::first($origContactIds); |
| 94 | } |
| 95 | |
| 96 | // FIXME: Refactor as separate method to get contactId |
| 97 | if (count($origContactIds) > 1) { |
| 98 | // check valid orig contact id |
| 99 | if (empty($params['orig_contact_id'])) { |
| 100 | throw new API_Exception('Case is linked with more than one contact id. Provide the required params orig_contact_id to be replaced'); |
| 101 | } |
| 102 | if (!empty($params['orig_contact_id']) && !in_array($params['orig_contact_id'], $origContactIds)) { |
| 103 | throw new API_Exception('Invalid case contact id (orig_contact_id)'); |
| 104 | } |
| 105 | $origContactId = $params['orig_contact_id']; |
| 106 | } |
| 107 | |
| 108 | // check for same contact id for edit Client |
| 109 | if (!empty($params['contact_id']) && !in_array($params['contact_id'], $origContactIds)) { |
| 110 | $mergedCaseId = CRM_Case_BAO_Case::mergeCases($params['contact_id'], $params['case_id'], $origContactId, NULL, TRUE); |
| 111 | } |
| 112 | |
| 113 | // If we merged cases then update the merged case |
| 114 | if (!empty($mergedCaseId[0])) { |
| 115 | $params['id'] = $mergedCaseId[0]; |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | // Create/update the case |
| 120 | $caseBAO = CRM_Case_BAO_Case::create($params); |
| 121 | |
| 122 | if (!$caseBAO) { |
| 123 | throw new API_Exception('Case not created. Please check input params.'); |
| 124 | } |
| 125 | |
| 126 | if (isset($params['contact_id'])) { |
| 127 | foreach ((array) $params['contact_id'] as $cid) { |
| 128 | $contactParams = array('case_id' => $caseBAO->id, 'contact_id' => $cid); |
| 129 | CRM_Case_BAO_CaseContact::create($contactParams); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | if (!isset($params['id'])) { |
| 134 | // As the API was not passed an id we have created a new case. |
| 135 | // Only run the xmlProcessor for new cases to get all configuration for the new case. |
| 136 | _civicrm_api3_case_create_xmlProcessor($params, $caseBAO); |
| 137 | } |
| 138 | |
| 139 | // return case |
| 140 | $values = array(); |
| 141 | _civicrm_api3_object_to_array($caseBAO, $values[$caseBAO->id]); |
| 142 | |
| 143 | return civicrm_api3_create_success($values, $params, 'Case', 'create', $caseBAO); |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * When creating a new case, run the xmlProcessor to get all necessary params/configuration |
| 148 | * for the new case, as cases use an xml file to store their configuration. |
| 149 | * @param $params |
| 150 | * @param $caseBAO |
| 151 | */ |
| 152 | function _civicrm_api3_case_create_xmlProcessor($params, $caseBAO) { |
| 153 | // Format params for xmlProcessor |
| 154 | if (isset($caseBAO->id)) { |
| 155 | $params['id'] = $caseBAO->id; |
| 156 | } |
| 157 | |
| 158 | // Initialize XML processor with $params |
| 159 | $xmlProcessor = new CRM_Case_XMLProcessor_Process(); |
| 160 | $xmlProcessorParams = array( |
| 161 | 'clientID' => CRM_Utils_Array::value('contact_id', $params), |
| 162 | 'creatorID' => CRM_Utils_Array::value('creator_id', $params), |
| 163 | 'standardTimeline' => 1, |
| 164 | 'activityTypeName' => 'Open Case', |
| 165 | 'caseID' => CRM_Utils_Array::value('id', $params), |
| 166 | 'subject' => CRM_Utils_Array::value('subject', $params), |
| 167 | 'location' => CRM_Utils_Array::value('location', $params), |
| 168 | 'activity_date_time' => CRM_Utils_Array::value('start_date', $params), |
| 169 | 'duration' => CRM_Utils_Array::value('duration', $params), |
| 170 | 'medium_id' => CRM_Utils_Array::value('medium_id', $params), |
| 171 | 'details' => CRM_Utils_Array::value('details', $params), |
| 172 | 'custom' => array(), |
| 173 | ); |
| 174 | |
| 175 | // Do it! :-D |
| 176 | $xmlProcessor->run($params['case_type'], $xmlProcessorParams); |
| 177 | } |
| 178 | |
| 179 | /** |
| 180 | * Adjust Metadata for Get Action. |
| 181 | * |
| 182 | * @param array $params |
| 183 | * Parameters determined by getfields. |
| 184 | */ |
| 185 | function _civicrm_api3_case_get_spec(&$params) { |
| 186 | $params['contact_id'] = array( |
| 187 | 'api.aliases' => array('client_id'), |
| 188 | 'title' => 'Case Client', |
| 189 | 'description' => 'Contact id of one or more clients to retrieve cases for', |
| 190 | 'type' => CRM_Utils_Type::T_INT, |
| 191 | ); |
| 192 | $params['activity_id'] = array( |
| 193 | 'title' => 'Case Activity', |
| 194 | 'description' => 'Id of an activity in the case', |
| 195 | 'type' => CRM_Utils_Type::T_INT, |
| 196 | ); |
| 197 | $params['tag_id'] = array( |
| 198 | 'title' => 'Tags', |
| 199 | 'description' => 'Find activities with specified tags.', |
| 200 | 'type' => 1, |
| 201 | 'FKClassName' => 'CRM_Core_DAO_Tag', |
| 202 | 'FKApiName' => 'Tag', |
| 203 | 'supports_joins' => TRUE, |
| 204 | ); |
| 205 | } |
| 206 | |
| 207 | /** |
| 208 | * Adjust Metadata for Create Action. |
| 209 | * |
| 210 | * @param array $params |
| 211 | * Array of parameters determined by getfields. |
| 212 | */ |
| 213 | function _civicrm_api3_case_create_spec(&$params) { |
| 214 | $params['contact_id'] = array( |
| 215 | 'api.aliases' => array('client_id'), |
| 216 | 'title' => 'Case Client', |
| 217 | 'description' => 'Contact id of case client(s)', |
| 218 | 'api.required' => 1, |
| 219 | 'type' => CRM_Utils_Type::T_INT, |
| 220 | ); |
| 221 | $params['status_id']['api.default'] = 1; |
| 222 | $params['status_id']['api.aliases'] = array('case_status'); |
| 223 | $params['creator_id']['api.default'] = 'user_contact_id'; |
| 224 | $params['creator_id']['type'] = CRM_Utils_Type::T_INT; |
| 225 | $params['creator_id']['title'] = 'Case Created By'; |
| 226 | $params['start_date']['api.default'] = 'now'; |
| 227 | $params['medium_id'] = array( |
| 228 | 'name' => 'medium_id', |
| 229 | 'title' => 'Activity Medium', |
| 230 | 'type' => CRM_Utils_Type::T_INT, |
| 231 | ); |
| 232 | } |
| 233 | |
| 234 | /** |
| 235 | * Adjust Metadata for Update action. |
| 236 | * |
| 237 | * @param array $params |
| 238 | * Array of parameters determined by getfields. |
| 239 | */ |
| 240 | function _civicrm_api3_case_update_spec(&$params) { |
| 241 | $params['id']['api.required'] = 1; |
| 242 | } |
| 243 | |
| 244 | /** |
| 245 | * Adjust Metadata for Delete action. |
| 246 | * |
| 247 | * @param array $params |
| 248 | * Array of parameters determined by getfields. |
| 249 | */ |
| 250 | function _civicrm_api3_case_delete_spec(&$params) { |
| 251 | $params['id']['api.required'] = 1; |
| 252 | } |
| 253 | |
| 254 | /** |
| 255 | * Get details of a particular case, or search for cases, depending on params. |
| 256 | * |
| 257 | * Please provide one (and only one) of the four get/search parameters: |
| 258 | * |
| 259 | * @param array $params |
| 260 | * 'id' => if set, will get all available info about a case, including contacts and activities |
| 261 | * |
| 262 | * // if no case_id provided, this function will use one of the following search parameters: |
| 263 | * 'client_id' => finds all cases with a specific client |
| 264 | * 'activity_id' => returns the case containing a specific activity |
| 265 | * 'contact_id' => finds all cases associated with a contact (in any role, not just client) |
| 266 | * $params CRM_Utils_SQL_Select $sql |
| 267 | * Other apis wishing to wrap & extend this one can pass in a $sql object with extra clauses |
| 268 | * |
| 269 | * @throws API_Exception |
| 270 | * @return array |
| 271 | * (get mode, case_id provided): Array with case details, case roles, case activity ids, (search mode, case_id not provided): Array of cases found |
| 272 | */ |
| 273 | function civicrm_api3_case_get($params, $sql = NULL) { |
| 274 | $options = _civicrm_api3_get_options_from_params($params); |
| 275 | if (!is_a($sql, 'CRM_Utils_SQL_Select')) { |
| 276 | $sql = CRM_Utils_SQL_Select::fragment(); |
| 277 | } |
| 278 | |
| 279 | // Add clause to search by client |
| 280 | if (!empty($params['contact_id'])) { |
| 281 | // Legacy support - this field historically supports a nonstandard format of array(1,2,3) as a synonym for array('IN' => array(1,2,3)) |
| 282 | if (is_array($params['contact_id'])) { |
| 283 | $operator = CRM_Utils_Array::first(array_keys($params['contact_id'])); |
| 284 | if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { |
| 285 | $params['contact_id'] = array('IN' => $params['contact_id']); |
| 286 | } |
| 287 | } |
| 288 | else { |
| 289 | $params['contact_id'] = array('=' => $params['contact_id']); |
| 290 | } |
| 291 | $clause = CRM_Core_DAO::createSQLFilter('contact_id', $params['contact_id']); |
| 292 | $sql->where("a.id IN (SELECT case_id FROM civicrm_case_contact WHERE $clause)"); |
| 293 | } |
| 294 | |
| 295 | // Order by case contact (primary client) |
| 296 | // Ex: "contact_id", "contact_id.display_name", "contact_id.sort_name DESC". |
| 297 | if (!empty($options['sort']) && strpos($options['sort'], 'contact_id') !== FALSE) { |
| 298 | $sort = explode(', ', $options['sort']); |
| 299 | $contactSort = NULL; |
| 300 | foreach ($sort as $index => &$sortString) { |
| 301 | if (strpos($sortString, 'contact_id') === 0) { |
| 302 | $contactSort = $sortString; |
| 303 | $sortString = '(1)'; |
| 304 | // Get sort field and direction |
| 305 | list($sortField, $dir) = array_pad(explode(' ', $contactSort), 2, 'ASC'); |
| 306 | list(, $sortField) = array_pad(explode('.', $sortField), 2, 'id'); |
| 307 | // Validate inputs |
| 308 | if (!array_key_exists($sortField, CRM_Contact_DAO_Contact::fieldKeys()) || ($dir != 'ASC' && $dir != 'DESC')) { |
| 309 | throw new API_Exception("Unknown field specified for sort. Cannot order by '$contactSort'"); |
| 310 | } |
| 311 | $sql->orderBy("case_contact.$sortField $dir", NULL, $index); |
| 312 | } |
| 313 | } |
| 314 | // Remove contact sort params so the basic_get function doesn't see them |
| 315 | $params['options']['sort'] = implode(', ', $sort); |
| 316 | unset($params['option_sort'], $params['option.sort'], $params['sort']); |
| 317 | // Add necessary joins to the first case client |
| 318 | if ($contactSort) { |
| 319 | $sql->join('ccc', 'LEFT JOIN (SELECT * FROM civicrm_case_contact WHERE id IN (SELECT MIN(id) FROM civicrm_case_contact GROUP BY case_id)) AS ccc ON ccc.case_id = a.id'); |
| 320 | $sql->join('case_contact', 'LEFT JOIN civicrm_contact AS case_contact ON ccc.contact_id = case_contact.id AND case_contact.is_deleted <> 1'); |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | // Add clause to search by activity |
| 325 | if (!empty($params['activity_id'])) { |
| 326 | if (!CRM_Utils_Rule::positiveInteger($params['activity_id'])) { |
| 327 | throw new API_Exception('Invalid parameter: activity_id. Must provide a numeric value.'); |
| 328 | } |
| 329 | $activityId = $params['activity_id']; |
| 330 | $originalId = CRM_Core_DAO::getFieldValue('CRM_Activity_BAO_Activity', $activityId, 'original_id'); |
| 331 | if ($originalId) { |
| 332 | $activityId .= ',' . $originalId; |
| 333 | } |
| 334 | $sql |
| 335 | ->join('civicrm_case_activity', 'INNER JOIN civicrm_case_activity ON civicrm_case_activity.case_id = a.id') |
| 336 | ->where("civicrm_case_activity.activity_id IN ($activityId)"); |
| 337 | } |
| 338 | |
| 339 | // Clause to search by tag |
| 340 | if (!empty($params['tag_id'])) { |
| 341 | $dummySpec = array(); |
| 342 | _civicrm_api3_validate_integer($params, 'tag_id', $dummySpec, 'Case'); |
| 343 | if (!is_array($params['tag_id'])) { |
| 344 | $params['tag_id'] = array('=' => $params['tag_id']); |
| 345 | } |
| 346 | $clause = \CRM_Core_DAO::createSQLFilter('tag_id', $params['tag_id']); |
| 347 | if ($clause) { |
| 348 | $sql->where('a.id IN (SELECT entity_id FROM civicrm_entity_tag WHERE entity_table = "civicrm_case" AND !clause)', array('!clause' => $clause)); |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | $cases = _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), array('sequential' => 0) + $params, TRUE, 'Case', $sql); |
| 353 | |
| 354 | if (empty($options['is_count']) && !empty($cases['values'])) { |
| 355 | // For historic reasons we return these by default only when fetching a case by id |
| 356 | if (!empty($params['id']) && is_numeric($params['id']) && empty($options['return'])) { |
| 357 | $options['return'] = array( |
| 358 | 'contacts' => 1, |
| 359 | 'activities' => 1, |
| 360 | 'contact_id' => 1, |
| 361 | ); |
| 362 | } |
| 363 | |
| 364 | _civicrm_api3_case_read($cases['values'], $options); |
| 365 | |
| 366 | // We disabled sequential to keep the list indexed for case_read(). Now add it back. |
| 367 | if (!empty($params['sequential'])) { |
| 368 | $cases['values'] = array_values($cases['values']); |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | return $cases; |
| 373 | } |
| 374 | |
| 375 | /** |
| 376 | * Deprecated API. |
| 377 | * |
| 378 | * Use activity API instead. |
| 379 | * |
| 380 | * @param array $params |
| 381 | * |
| 382 | * @throws API_Exception |
| 383 | * @return array |
| 384 | */ |
| 385 | function civicrm_api3_case_activity_create($params) { |
| 386 | require_once "api/v3/Activity.php"; |
| 387 | return civicrm_api3_activity_create($params) + array( |
| 388 | 'deprecated' => CRM_Utils_Array::value('activity_create', _civicrm_api3_case_deprecation()), |
| 389 | ); |
| 390 | } |
| 391 | |
| 392 | /** |
| 393 | * Add a timeline to a case. |
| 394 | * |
| 395 | * @param array $params |
| 396 | * |
| 397 | * @throws API_Exception |
| 398 | * @return array |
| 399 | */ |
| 400 | function civicrm_api3_case_addtimeline($params) { |
| 401 | $caseType = CRM_Case_BAO_Case::getCaseType($params['case_id'], 'name'); |
| 402 | $xmlProcessor = new CRM_Case_XMLProcessor_Process(); |
| 403 | $xmlProcessorParams = array( |
| 404 | 'clientID' => CRM_Case_BAO_Case::getCaseClients($params['case_id']), |
| 405 | 'creatorID' => $params['creator_id'], |
| 406 | 'standardTimeline' => 0, |
| 407 | 'activity_date_time' => $params['activity_date_time'], |
| 408 | 'caseID' => $params['case_id'], |
| 409 | 'caseType' => $caseType, |
| 410 | 'activitySetName' => $params['timeline'], |
| 411 | ); |
| 412 | $xmlProcessor->run($caseType, $xmlProcessorParams); |
| 413 | return civicrm_api3_create_success(); |
| 414 | } |
| 415 | |
| 416 | /** |
| 417 | * Adjust Metadata for addtimeline action. |
| 418 | * |
| 419 | * @param array $params |
| 420 | * Array of parameters determined by getfields. |
| 421 | */ |
| 422 | function _civicrm_api3_case_addtimeline_spec(&$params) { |
| 423 | $params['case_id'] = array( |
| 424 | 'title' => 'Case ID', |
| 425 | 'description' => 'Id of case to update', |
| 426 | 'type' => CRM_Utils_Type::T_INT, |
| 427 | 'api.required' => 1, |
| 428 | ); |
| 429 | $params['timeline'] = array( |
| 430 | 'title' => 'Timeline', |
| 431 | 'description' => 'Name of activity set', |
| 432 | 'type' => CRM_Utils_Type::T_STRING, |
| 433 | 'api.required' => 1, |
| 434 | ); |
| 435 | $params['activity_date_time'] = array( |
| 436 | 'api.default' => 'now', |
| 437 | 'title' => 'Activity date time', |
| 438 | 'description' => 'Timeline start date', |
| 439 | 'type' => CRM_Utils_Type::T_DATE, |
| 440 | ); |
| 441 | $params['creator_id'] = array( |
| 442 | 'api.default' => 'user_contact_id', |
| 443 | 'title' => 'Activity creator', |
| 444 | 'description' => 'Contact id of timeline creator', |
| 445 | 'type' => CRM_Utils_Type::T_INT, |
| 446 | ); |
| 447 | } |
| 448 | |
| 449 | /** |
| 450 | * Merge 2 cases. |
| 451 | * |
| 452 | * @param array $params |
| 453 | * |
| 454 | * @throws API_Exception |
| 455 | * @return array |
| 456 | */ |
| 457 | function civicrm_api3_case_merge($params) { |
| 458 | $clients1 = CRM_Case_BAO_Case::getCaseClients($params['case_id_1']); |
| 459 | $clients2 = CRM_Case_BAO_Case::getCaseClients($params['case_id_2']); |
| 460 | CRM_Case_BAO_Case::mergeCases($clients1[0], $params['case_id_1'], $clients2[0], $params['case_id_2']); |
| 461 | return civicrm_api3_create_success(); |
| 462 | } |
| 463 | |
| 464 | /** |
| 465 | * Adjust Metadata for merge action. |
| 466 | * |
| 467 | * @param array $params |
| 468 | * Array of parameters determined by getfields. |
| 469 | */ |
| 470 | function _civicrm_api3_case_merge_spec(&$params) { |
| 471 | $params['case_id_1'] = array( |
| 472 | 'title' => 'Case ID 1', |
| 473 | 'description' => 'Id of main case', |
| 474 | 'type' => CRM_Utils_Type::T_INT, |
| 475 | 'api.required' => 1, |
| 476 | ); |
| 477 | $params['case_id_2'] = array( |
| 478 | 'title' => 'Case ID 2', |
| 479 | 'description' => 'Id of second case', |
| 480 | 'type' => CRM_Utils_Type::T_INT, |
| 481 | 'api.required' => 1, |
| 482 | ); |
| 483 | } |
| 484 | |
| 485 | /** |
| 486 | * Declare deprecated api functions. |
| 487 | * |
| 488 | * @deprecated api notice |
| 489 | * @return array |
| 490 | * Array of deprecated actions |
| 491 | */ |
| 492 | function _civicrm_api3_case_deprecation() { |
| 493 | return array('activity_create' => 'Case api "activity_create" action is deprecated. Use the activity api instead.'); |
| 494 | } |
| 495 | |
| 496 | /** |
| 497 | * @deprecated Update a specified case. Use civicrm_api3_case_create() instead. |
| 498 | * |
| 499 | * @param array $params |
| 500 | * //REQUIRED: |
| 501 | * 'case_id' => int |
| 502 | * |
| 503 | * //OPTIONAL |
| 504 | * 'status_id' => int |
| 505 | * 'start_date' => str datestamp |
| 506 | * 'contact_id' => int // case client |
| 507 | * |
| 508 | * @throws API_Exception |
| 509 | * @return array |
| 510 | * api result array |
| 511 | */ |
| 512 | function civicrm_api3_case_update($params) { |
| 513 | if (!isset($params['case_id']) && isset($params['id'])) { |
| 514 | $params['case_id'] = $params['id']; |
| 515 | } |
| 516 | |
| 517 | //check parameters |
| 518 | civicrm_api3_verify_mandatory($params, NULL, array('id')); |
| 519 | |
| 520 | // return error if modifying creator id |
| 521 | if (array_key_exists('creator_id', $params)) { |
| 522 | throw new API_Exception(ts('You cannot update creator id')); |
| 523 | } |
| 524 | |
| 525 | $mCaseId = $origContactIds = array(); |
| 526 | |
| 527 | // get original contact id and creator id of case |
| 528 | if (!empty($params['contact_id'])) { |
| 529 | $origContactIds = CRM_Case_BAO_Case::retrieveContactIdsByCaseId($params['id']); |
| 530 | $origContactId = CRM_Utils_Array::first($origContactIds); |
| 531 | } |
| 532 | |
| 533 | if (count($origContactIds) > 1) { |
| 534 | // check valid orig contact id |
| 535 | if (!empty($params['orig_contact_id']) && !in_array($params['orig_contact_id'], $origContactIds)) { |
| 536 | throw new API_Exception('Invalid case contact id (orig_contact_id)'); |
| 537 | } |
| 538 | elseif (empty($params['orig_contact_id'])) { |
| 539 | throw new API_Exception('Case is linked with more than one contact id. Provide the required params orig_contact_id to be replaced'); |
| 540 | } |
| 541 | $origContactId = $params['orig_contact_id']; |
| 542 | } |
| 543 | |
| 544 | // check for same contact id for edit Client |
| 545 | if (!empty($params['contact_id']) && !in_array($params['contact_id'], $origContactIds)) { |
| 546 | $mCaseId = CRM_Case_BAO_Case::mergeCases($params['contact_id'], $params['case_id'], $origContactId, NULL, TRUE); |
| 547 | } |
| 548 | |
| 549 | if (!empty($mCaseId[0])) { |
| 550 | $params['id'] = $mCaseId[0]; |
| 551 | } |
| 552 | |
| 553 | $dao = new CRM_Case_BAO_Case(); |
| 554 | $dao->id = $params['id']; |
| 555 | |
| 556 | $dao->copyValues($params); |
| 557 | $dao->save(); |
| 558 | |
| 559 | $case = array(); |
| 560 | _civicrm_api3_object_to_array($dao, $case); |
| 561 | |
| 562 | return civicrm_api3_create_success(array($dao->id => $case), $params, 'Case', 'update', $dao); |
| 563 | } |
| 564 | |
| 565 | /** |
| 566 | * Delete a specified case. |
| 567 | * |
| 568 | * @param array $params |
| 569 | * |
| 570 | * @code |
| 571 | * //REQUIRED: |
| 572 | * 'id' => int |
| 573 | * |
| 574 | * //OPTIONAL |
| 575 | * 'move_to_trash' => bool (defaults to false) |
| 576 | * @endcode |
| 577 | * |
| 578 | * @throws API_Exception |
| 579 | * @return mixed |
| 580 | */ |
| 581 | function civicrm_api3_case_delete($params) { |
| 582 | //check parameters |
| 583 | civicrm_api3_verify_mandatory($params, NULL, array('id')); |
| 584 | |
| 585 | if (CRM_Case_BAO_Case::deleteCase($params['id'], CRM_Utils_Array::value('move_to_trash', $params, FALSE))) { |
| 586 | return civicrm_api3_create_success($params, $params, 'Case', 'delete'); |
| 587 | } |
| 588 | else { |
| 589 | throw new API_Exception('Could not delete case.'); |
| 590 | } |
| 591 | } |
| 592 | |
| 593 | /** |
| 594 | * Case.restore API specification |
| 595 | * |
| 596 | * @param array $spec description of fields supported by this API call |
| 597 | * @return void |
| 598 | */ |
| 599 | function _civicrm_api3_case_restore_spec(&$spec) { |
| 600 | $result = civicrm_api3('Case', 'getfields', array('api_action' => 'delete')); |
| 601 | $spec = array('id' => $result['values']['id']); |
| 602 | } |
| 603 | |
| 604 | /** |
| 605 | * Restore a specified case from the trash. |
| 606 | * |
| 607 | * @param array $params |
| 608 | * @throws API_Exception |
| 609 | * @return mixed |
| 610 | */ |
| 611 | function civicrm_api3_case_restore($params) { |
| 612 | if (CRM_Case_BAO_Case::restoreCase($params['id'])) { |
| 613 | return civicrm_api3_create_success($params, $params, 'Case', 'restore'); |
| 614 | } |
| 615 | else { |
| 616 | throw new API_Exception('Could not restore case.'); |
| 617 | } |
| 618 | } |
| 619 | |
| 620 | /** |
| 621 | * Augment case results with extra data. |
| 622 | * |
| 623 | * @param array $cases |
| 624 | * @param array $options |
| 625 | */ |
| 626 | function _civicrm_api3_case_read(&$cases, $options) { |
| 627 | foreach ($cases as &$case) { |
| 628 | if (empty($options['return']) || !empty($options['return']['contact_id'])) { |
| 629 | // Legacy support for client_id - TODO: in apiv4 remove 'client_id' |
| 630 | $case['client_id'] = $case['contact_id'] = CRM_Case_BAO_Case::retrieveContactIdsByCaseId($case['id']); |
| 631 | } |
| 632 | if (!empty($options['return']['contacts'])) { |
| 633 | //get case contacts |
| 634 | $contacts = CRM_Case_BAO_Case::getcontactNames($case['id']); |
| 635 | $relations = CRM_Case_BAO_Case::getRelatedContacts($case['id']); |
| 636 | $case['contacts'] = array_unique(array_merge($contacts, $relations), SORT_REGULAR); |
| 637 | } |
| 638 | if (!empty($options['return']['activities'])) { |
| 639 | // add case activities array - we'll populate them in bulk below |
| 640 | $case['activities'] = array(); |
| 641 | } |
| 642 | // Properly render this joined field |
| 643 | if (!empty($options['return']['case_type_id.definition'])) { |
| 644 | if (!empty($case['case_type_id.definition'])) { |
| 645 | list($xml) = CRM_Utils_XML::parseString($case['case_type_id.definition']); |
| 646 | } |
| 647 | else { |
| 648 | $caseTypeId = !empty($case['case_type_id']) ? $case['case_type_id'] : CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $case['id'], 'case_type_id'); |
| 649 | $caseTypeName = !empty($case['case_type_id.name']) ? $case['case_type_id.name'] : CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'name'); |
| 650 | $xml = CRM_Case_XMLRepository::singleton()->retrieve($caseTypeName); |
| 651 | } |
| 652 | $case['case_type_id.definition'] = array(); |
| 653 | if ($xml) { |
| 654 | $case['case_type_id.definition'] = CRM_Case_BAO_CaseType::convertXmlToDefinition($xml); |
| 655 | } |
| 656 | } |
| 657 | } |
| 658 | // Bulk-load activities |
| 659 | if (!empty($options['return']['activities'])) { |
| 660 | $query = "SELECT case_id, activity_id FROM civicrm_case_activity WHERE case_id IN (%1)"; |
| 661 | $params = array(1 => array(implode(',', array_keys($cases)), 'String', CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES)); |
| 662 | $dao = CRM_Core_DAO::executeQuery($query, $params); |
| 663 | while ($dao->fetch()) { |
| 664 | $cases[$dao->case_id]['activities'][] = $dao->activity_id; |
| 665 | } |
| 666 | } |
| 667 | // Bulk-load tags. Supports joins onto the tag entity. |
| 668 | $tagGet = array('tag_id', 'entity_id'); |
| 669 | foreach (array_keys($options['return']) as $key) { |
| 670 | if (strpos($key, 'tag_id.') === 0) { |
| 671 | $tagGet[] = $key; |
| 672 | $options['return']['tag_id'] = 1; |
| 673 | } |
| 674 | } |
| 675 | if (!empty($options['return']['tag_id'])) { |
| 676 | $tags = civicrm_api3('EntityTag', 'get', array( |
| 677 | 'entity_table' => 'civicrm_case', |
| 678 | 'entity_id' => array('IN' => array_keys($cases)), |
| 679 | 'return' => $tagGet, |
| 680 | 'options' => array('limit' => 0), |
| 681 | )); |
| 682 | foreach ($tags['values'] as $tag) { |
| 683 | $key = (int) $tag['entity_id']; |
| 684 | unset($tag['entity_id'], $tag['id']); |
| 685 | $cases[$key]['tag_id'][$tag['tag_id']] = $tag; |
| 686 | } |
| 687 | } |
| 688 | } |
| 689 | |
| 690 | /** |
| 691 | * Internal function to format create params for processing. |
| 692 | * |
| 693 | * @param array $params |
| 694 | */ |
| 695 | function _civicrm_api3_case_format_params(&$params) { |
| 696 | // Format/include custom params |
| 697 | $values = array(); |
| 698 | _civicrm_api3_custom_format_params($params, $values, 'Case'); |
| 699 | $params = array_merge($params, $values); |
| 700 | |
| 701 | if (empty($params['case_type_id']) && empty($params['case_type'])) { |
| 702 | return; |
| 703 | } |
| 704 | |
| 705 | // figure out case_type_id from case_type and vice-versa |
| 706 | $caseTypes = CRM_Case_PseudoConstant::caseType('name', FALSE); |
| 707 | if (empty($params['case_type_id'])) { |
| 708 | $params['case_type_id'] = array_search($params['case_type'], $caseTypes); |
| 709 | |
| 710 | // DEPRECATED: lookup by label for backward compatibility |
| 711 | if (!$params['case_type_id']) { |
| 712 | $caseTypeLabels = CRM_Case_PseudoConstant::caseType('title', FALSE); |
| 713 | $params['case_type_id'] = array_search($params['case_type'], $caseTypeLabels); |
| 714 | $params['case_type'] = $caseTypes[$params['case_type_id']]; |
| 715 | } |
| 716 | } |
| 717 | elseif (empty($params['case_type'])) { |
| 718 | $params['case_type'] = $caseTypes[$params['case_type_id']]; |
| 719 | } |
| 720 | } |
| 721 | |
| 722 | |
| 723 | /** |
| 724 | * It actually works a lot better to use the CaseContact api instead of the Case api |
| 725 | * for entityRef fields so we can perform the necessary joins, |
| 726 | * so we pass off getlist requests to the CaseContact api. |
| 727 | * |
| 728 | * @param array $params |
| 729 | * @return mixed |
| 730 | */ |
| 731 | function civicrm_api3_case_getList($params) { |
| 732 | require_once 'api/v3/Generic/Getlist.php'; |
| 733 | require_once 'api/v3/CaseContact.php'; |
| 734 | //CRM:19956 - Assign case_id param if both id and case_id is passed to retrieve the case |
| 735 | if (!empty($params['id']) && !empty($params['params']) && !empty($params['params']['case_id'])) { |
| 736 | $params['params']['case_id'] = array('IN' => $params['id']); |
| 737 | unset($params['id']); |
| 738 | } |
| 739 | $params['id_field'] = 'case_id'; |
| 740 | $params['label_field'] = $params['search_field'] = 'contact_id.sort_name'; |
| 741 | $params['description_field'] = array( |
| 742 | 'case_id', |
| 743 | 'case_id.case_type_id.title', |
| 744 | 'case_id.subject', |
| 745 | 'case_id.status_id', |
| 746 | 'case_id.start_date', |
| 747 | ); |
| 748 | $apiRequest = array( |
| 749 | 'version' => 3, |
| 750 | 'entity' => 'CaseContact', |
| 751 | 'action' => 'getlist', |
| 752 | 'params' => $params, |
| 753 | ); |
| 754 | return civicrm_api3_generic_getList($apiRequest); |
| 755 | } |
| 756 | |
| 757 | /** |
| 758 | * Needed due to the above override |
| 759 | * @param $params |
| 760 | * @param $apiRequest |
| 761 | */ |
| 762 | function _civicrm_api3_case_getlist_spec(&$params, $apiRequest) { |
| 763 | require_once 'api/v3/Generic/Getlist.php'; |
| 764 | _civicrm_api3_generic_getlist_spec($params, $apiRequest); |
| 765 | } |