static function formRule($fields) { $errors = array(); require_once 'api/api.php'; $apiRequest = array(); $apiRequest['entity'] = CRM_Utils_String::munge($fields['api_entity']); $apiRequest['action'] = CRM_Utils_String::munge($fields['api_action']); $apiRequest['version'] = substr($fields['api_prefix'], -1); // ie. civicrm_api3 $apiRequest += _civicrm_api_resolve($apiRequest); // look up function, file, is_generic if (!$apiRequest['function']) { $errors['api_action'] = ts('Given API command is not defined.'); } // CRM-9868- don't allow Enabled (is_active) for jobs that should never be run automatically via execute action or runjobs url if (($fields['api_action'] == 'process_membership_reminder_date' || $fields['api_action'] == 'update_greeting') && CRM_Utils_Array::value('is_active', $fields) == 1) { // pass "wiki" as 6th param to docURL2 if you are linking to a page in wiki.civicrm.org $docLink = CRM_Utils_System::docURL2("Managing Scheduled Jobs", NULL, NULL, NULL, NULL, "wiki"); $errors['is_active'] = ts('You can not save this Scheduled Job as Active with the specified api action (%2). That action should not be run regularly - it should only be run manually for special conditions. %1', array(1 => $docLink, 2 => $fields['api_action'])); } if (!empty($errors)) { return $errors; } return empty($errors) ? TRUE : $errors; }
static function formRule($fields) { $errors = array(); require_once 'api/api.php'; $apiRequest = array(); $apiRequest['entity'] = CRM_Utils_String::munge($fields['api_entity']); $apiRequest['action'] = CRM_Utils_String::munge($fields['api_action']); $apiRequest['version'] = 3; $apiRequest += _civicrm_api_resolve($apiRequest); // look up function, file, is_generic if (!$apiRequest['function']) { $errors['api_action'] = ts('Given API command is not defined.'); } if (!empty($errors)) { return $errors; } return empty($errors) ? TRUE : $errors; }
/** * Get information about fields for a given api request. Getfields information * is used for documentation, validation, default setting * We first query the scheme using the $dao->fields function & then augment * that information by calling the _spec functions that apply to the relevant function * Note that we use 'unique' field names as described in the xml/schema files * for get requests & just field name for create. This is because some get functions * access multiple objects e.g. contact api accesses is_deleted from the activity * table & from the contact table * * @param array $apiRequest api request as an array. Keys are * - entity: string * - action: string * - version: string * - function: callback (mixed) * - params: array, varies * @return array API success object */ function civicrm_api3_generic_getfields($apiRequest) { static $results = array(); if (CRM_Utils_Array::value('cache_clear', $apiRequest['params'])) { $results = array(); // we will also clear pseudoconstants here - should potentially be moved to relevant BAO classes CRM_Core_PseudoConstant::flush(); if (!empty($apiRequest['params']['fieldname'])) { CRM_Utils_PseudoConstant::flushConstant($apiRequest['params']['fieldname']); } if (!empty($apiRequest['params']['option_group_id'])) { $optionGroupName = civicrm_api('option_group', 'getvalue', array('version' => 3, 'id' => $apiRequest['params']['option_group_id'], 'return' => 'name')); if (is_string($optionGroupName)) { CRM_Utils_PseudoConstant::flushConstant(_civicrm_api_get_camel_name($optionGroupName)); } } } $entity = _civicrm_api_get_camel_name($apiRequest['entity']); $lcase_entity = _civicrm_api_get_entity_name_from_camel($entity); $subentity = CRM_Utils_Array::value('contact_type', $apiRequest['params']); $action = strtolower(CRM_Utils_Array::value('action', $apiRequest['params'])); $sequential = empty($apiRequest['params']) ? 0 : 1; $apiOptions = CRM_Utils_Array::value('options', $apiRequest['params'], array()); if ($action == 'getvalue' || $action == 'getvalue' || $action == 'getcount') { $action = 'get'; } if (empty($action)) { $action = 'get'; } // determines whether to use unique field names - seem comment block above $unique = TRUE; if (empty($apiOptions) && isset($results[$entity . $subentity]) && isset($action, $results[$entity . $subentity]) && isset($action, $results[$entity . $subentity][$sequential])) { return $results[$entity . $subentity][$action][$sequential]; } // defaults based on data model and API policy switch ($action) { case 'getfields': $values = _civicrm_api_get_fields($entity, false, $apiRequest['params']); return civicrm_api3_create_success($values, $apiRequest['params'], $entity, 'getfields'); case 'create': case 'update': case 'replace': $unique = FALSE; case 'get': $metadata = _civicrm_api_get_fields($apiRequest['entity'], $unique, $apiRequest['params']); if (empty($metadata['id'])) { // if id is not set we will set it eg. 'id' from 'case_id', case_id will be an alias if (!empty($metadata[strtolower($apiRequest['entity']) . '_id'])) { $metadata['id'] = $metadata[$lcase_entity . '_id']; unset($metadata[$lcase_entity . '_id']); $metadata['id']['api.aliases'] = array($lcase_entity . '_id'); } } else { // really the preference would be to set the unique name in the xml // question is which is a less risky fix this close to a release - setting in xml for the known failure // (note) or setting for all api where fields is returning 'id' & we want to accept 'note_id' @ the api layer // nb we don't officially accept note_id anyway - rationale here is more about centralising a now-tested // inconsistency $metadata['id']['api.aliases'] = array($lcase_entity . '_id'); } break; case 'delete': $metadata = array('id' => array('title' => 'Unique Identifier', 'api.required' => 1, 'api.aliases' => array($lcase_entity . '_id'), 'type' => CRM_Utils_Type::T_INT)); break; case 'getoptions': $metadata = array('field' => array('title' => 'Field to retrieve options for', 'api.required' => 1), 'context' => array('title' => 'Context string')); break; default: // oddballs are on their own $metadata = array(); } // find any supplemental information $hypApiRequest = array('entity' => $apiRequest['entity'], 'action' => $action, 'version' => $apiRequest['version']); $hypApiRequest += _civicrm_api_resolve($hypApiRequest); $helper = '_' . $hypApiRequest['function'] . '_spec'; if (function_exists($helper)) { // alter $helper($metadata, $apiRequest); } $fieldsToResolve = (array) CRM_Utils_Array::value('get_options', $apiOptions, array()); foreach ($metadata as $fieldname => $fieldSpec) { _civicrm_api3_generic_get_metadata_options($metadata, $apiRequest['entity'], $fieldname, $fieldSpec, $fieldsToResolve); } $results[$entity][$action][$sequential] = civicrm_api3_create_success($metadata, $apiRequest['params'], NULL, 'getfields'); return $results[$entity][$action][$sequential]; }
/** * @param string $entity * type of entities to deal with * @param string $action * create, get, delete or some special action name. * @param array $params * array to be passed to function * @param null $extra * * @return array|int */ function civicrm_api($entity, $action, $params, $extra = NULL) { $apiRequest = array(); $apiRequest['entity'] = CRM_Utils_String::munge($entity); $apiRequest['action'] = CRM_Utils_String::munge($action); $apiRequest['version'] = civicrm_get_api_version($params); $apiRequest['params'] = $params; $apiRequest['extra'] = $extra; $apiWrappers = array(CRM_Utils_API_HTMLInputCoder::singleton(), CRM_Utils_API_NullOutputCoder::singleton(), CRM_Utils_API_ReloadOption::singleton(), CRM_Utils_API_MatchOption::singleton()); CRM_Utils_Hook::apiWrappers($apiWrappers, $apiRequest); try { require_once 'api/v3/utils.php'; require_once 'api/Exception.php'; if (!is_array($params)) { throw new API_Exception('Input variable `params` is not an array', 2000); } _civicrm_api3_initialize(); $errorScope = CRM_Core_TemporaryErrorScope::useException(); // look up function, file, is_generic $apiRequest += _civicrm_api_resolve($apiRequest); if (strtolower($action) == 'create' || strtolower($action) == 'delete' || strtolower($action) == 'submit') { $apiRequest['is_transactional'] = 1; $transaction = new CRM_Core_Transaction(); } // support multi-lingual requests if ($language = CRM_Utils_Array::value('option.language', $params)) { _civicrm_api_set_locale($language); } _civicrm_api3_api_check_permission($apiRequest['entity'], $apiRequest['action'], $apiRequest['params']); $fields = _civicrm_api3_api_getfields($apiRequest); // we do this before we _civicrm_api3_swap_out_aliases($apiRequest, $fields); if (strtolower($action) != 'getfields') { if (empty($apiRequest['params']['id'])) { $apiRequest['params'] = array_merge(_civicrm_api3_getdefaults($apiRequest, $fields), $apiRequest['params']); } //if 'id' is set then only 'version' will be checked but should still be checked for consistency civicrm_api3_verify_mandatory($apiRequest['params'], NULL, _civicrm_api3_getrequired($apiRequest, $fields)); } // For input filtering, process $apiWrappers in forward order foreach ($apiWrappers as $apiWrapper) { $apiRequest = $apiWrapper->fromApiInput($apiRequest); } $function = $apiRequest['function']; if ($apiRequest['function'] && $apiRequest['is_generic']) { // Unlike normal API implementations, generic implementations require explicit // knowledge of the entity and action (as well as $params). Bundle up these bits // into a convenient data structure. $result = $function($apiRequest); } elseif ($apiRequest['function'] && !$apiRequest['is_generic']) { _civicrm_api3_validate_fields($apiRequest['entity'], $apiRequest['action'], $apiRequest['params'], $fields); $result = isset($extra) ? $function($apiRequest['params'], $extra) : $function($apiRequest['params']); } else { return civicrm_api3_create_error("API (" . $apiRequest['entity'] . ", " . $apiRequest['action'] . ") does not exist (join the API team and implement it!)"); } // For output filtering, process $apiWrappers in reverse order foreach (array_reverse($apiWrappers) as $apiWrapper) { $result = $apiWrapper->toApiOutput($apiRequest, $result); } if (CRM_Utils_Array::value('format.is_success', $apiRequest['params']) == 1) { if ($result['is_error'] === 0) { return 1; } else { return 0; } } if (!empty($apiRequest['params']['format.only_id']) && isset($result['id'])) { return $result['id']; } if (CRM_Utils_Array::value('is_error', $result, 0) == 0) { _civicrm_api_call_nested_api($apiRequest['params'], $result, $apiRequest['action'], $apiRequest['entity'], $apiRequest['version']); } if (function_exists('xdebug_time_index') && CRM_Utils_Array::value('debug', $apiRequest['params']) && is_array($result)) { $result['xdebug']['peakMemory'] = xdebug_peak_memory_usage(); $result['xdebug']['memory'] = xdebug_memory_usage(); $result['xdebug']['timeIndex'] = xdebug_time_index(); } return $result; } catch (PEAR_Exception $e) { if (CRM_Utils_Array::value('format.is_success', $apiRequest['params']) == 1) { return 0; } $error = $e->getCause(); if ($error instanceof DB_Error) { $data["error_code"] = DB::errorMessage($error->getCode()); $data["sql"] = $error->getDebugInfo(); } if (!empty($apiRequest['params']['debug'])) { if (method_exists($e, 'getUserInfo')) { $data['debug_info'] = $error->getUserInfo(); } if (method_exists($e, 'getExtraData')) { $data['debug_info'] = $data + $error->getExtraData(); } $data['trace'] = $e->getTraceAsString(); } else { $data['tip'] = "add debug=1 to your API call to have more info about the error"; } $err = civicrm_api3_create_error($e->getMessage(), $data, $apiRequest); if (!empty($apiRequest['is_transactional'])) { $transaction->rollback(); } return $err; } catch (API_Exception $e) { if (!isset($apiRequest)) { $apiRequest = array(); } if (CRM_Utils_Array::value('format.is_success', CRM_Utils_Array::value('params', $apiRequest)) == 1) { return 0; } $data = $e->getExtraParams(); $data['entity'] = CRM_Utils_Array::value('entity', $apiRequest); $data['action'] = CRM_Utils_Array::value('action', $apiRequest); $err = civicrm_api3_create_error($e->getMessage(), $data, $apiRequest, $e->getCode()); if (CRM_Utils_Array::value('debug', CRM_Utils_Array::value('params', $apiRequest)) && empty($data['trace'])) { $err['trace'] = $e->getTraceAsString(); } if (!empty($apiRequest['is_transactional'])) { $transaction->rollback(); } return $err; } catch (Exception $e) { if (CRM_Utils_Array::value('format.is_success', $apiRequest['params']) == 1) { return 0; } $data = array(); $err = civicrm_api3_create_error($e->getMessage(), $data, $apiRequest, $e->getCode()); if (!empty($apiRequest['params']['debug'])) { $err['trace'] = $e->getTraceAsString(); } if (!empty($apiRequest['is_transactional'])) { $transaction->rollback(); } return $err; } }
/** * Get information about fields for a given api request. Getfields information * is used for documentation, validation, default setting * We first query the scheme using the $dao->fields function & then augment * that information by calling the _spec functions that apply to the relevant function * Note that we use 'unique' field names as described in the xml/schema files * for get requests & just field name for create. This is because some get functions * access multiple objects e.g. contact api accesses is_deleted from the activity * table & from the contact table * * @param array $apiRequest api request as an array. Keys are * - entity: string * - action: string * - version: string * - function: callback (mixed) * - params: array, varies * @return array API success object */ function civicrm_api3_generic_getfields($apiRequest) { static $results = array(); if (CRM_Utils_Array::value('cache_clear', $apiRequest['params'])) { $results = array(); } $entity = _civicrm_api_get_camel_name($apiRequest['entity']); $lcase_entity = _civicrm_api_get_entity_name_from_camel($entity); $subentity = CRM_Utils_Array::value('contact_type', $apiRequest['params']); $action = strtolower(CRM_Utils_Array::value('action', $apiRequest['params'])); if ($action == 'getvalue' || $action == 'getvalue' || $action == 'getcount') { $action = 'get'; } if (empty($action)) { if (CRM_Utils_Array::value($entity . $subentity, $results) && CRM_Utils_Array::value('values', $results[$entity . $subentity])) { return $results[$entity . $subentity]; } else { $values = _civicrm_api_get_fields($entity); $results[$entity] = civicrm_api3_create_success($values, $apiRequest['params'], $entity, 'getfields'); return $results[$entity]; } } // determines whether to use unique field names - seem comment block above $unique = TRUE; if (isset($results[$entity . $subentity]) && CRM_Utils_Array::value($action, $results[$entity])) { return $results[$entity . $subentity][$action]; } // defaults based on data model and API policy switch ($action) { case 'getfields': $values = _civicrm_api_get_fields($entity, $apiRequest['params']); $results[$entity][$action] = civicrm_api3_create_success($values, $apiRequest['params'], $entity, 'getfields'); return $results[$entity][$action]; case 'create': case 'update': case 'replace': $unique = FALSE; case 'get': $metadata = _civicrm_api_get_fields($apiRequest['entity'], $unique, $apiRequest['params']); if (empty($metadata['id']) && !empty($metadata[$apiRequest['entity'] . '_id'])) { $metadata['id'] = $metadata[$lcase_entity . '_id']; $metadata['id']['api.aliases'] = array($lcase_entity . '_id'); unset($metadata[$lcase_entity . '_id']); } break; case 'delete': $metadata = array('id' => array('title' => 'Unique Identifier', 'api.required' => 1, 'api.aliases' => array($lcase_entity . '_id'))); break; default: // oddballs are on their own $metadata = array(); } // find any supplemental information $hypApiRequest = array('entity' => $apiRequest['entity'], 'action' => $action, 'version' => $apiRequest['version']); $hypApiRequest += _civicrm_api_resolve($hypApiRequest); $helper = '_' . $hypApiRequest['function'] . '_spec'; if (function_exists($helper)) { // alter $helper($metadata); } foreach ($metadata as $fieldname => $field) { if (array_key_exists('pseudoconstant', $field) && !CRM_Utils_Array::value('FKClassName', $field)) { $options = civicrm_api('constant', 'get', array('version' => 3, 'name' => $field['pseudoconstant'])); if (is_array(CRM_Utils_Array::value('values', $options))) { $metadata[$fieldname]['options'] = $options['values']; } } if (array_key_exists('enumValues', $field)) { $metadata[$fieldname]['options'] = explode(',', $field['enumValues']); } } $results[$entity][$action] = civicrm_api3_create_success($metadata, $apiRequest['params'], NULL, 'getfields'); return $results[$entity][$action]; }
function civicrm_api($entity, $action, $params, $extra = NULL) { $apiWrappers = array(CRM_Core_HTMLInputCoder::singleton()); try { require_once 'api/v3/utils.php'; require_once 'api/Exception.php'; if (!is_array($params)) { throw new API_Exception('Input variable `params` is not an array', 2000); } _civicrm_api3_initialize(); $errorScope = CRM_Core_TemporaryErrorScope::useException(); require_once 'CRM/Utils/String.php'; require_once 'CRM/Utils/Array.php'; $apiRequest = array(); $apiRequest['entity'] = CRM_Utils_String::munge($entity); $apiRequest['action'] = CRM_Utils_String::munge($action); $apiRequest['version'] = civicrm_get_api_version($params); $apiRequest['params'] = $params; $apiRequest['extra'] = $extra; // look up function, file, is_generic $apiRequest += _civicrm_api_resolve($apiRequest); if (strtolower($action) == 'create' || strtolower($action) == 'delete') { $apiRequest['is_transactional'] = 1; $tx = new CRM_Core_Transaction(); } $errorFnName = $apiRequest['version'] == 2 ? 'civicrm_create_error' : 'civicrm_api3_create_error'; if ($apiRequest['version'] > 2) { _civicrm_api3_api_check_permission($apiRequest['entity'], $apiRequest['action'], $apiRequest['params']); } // we do this before we _civicrm_api3_swap_out_aliases($apiRequest); if (strtolower($action) != 'getfields') { if (!CRM_Utils_Array::value('id', $params)) { $apiRequest['params'] = array_merge(_civicrm_api3_getdefaults($apiRequest), $apiRequest['params']); } //if 'id' is set then only 'version' will be checked but should still be checked for consistency civicrm_api3_verify_mandatory($apiRequest['params'], NULL, _civicrm_api3_getrequired($apiRequest)); } foreach ($apiWrappers as $apiWrapper) { $apiRequest = $apiWrapper->fromApiInput($apiRequest); } $function = $apiRequest['function']; if ($apiRequest['function'] && $apiRequest['is_generic']) { // Unlike normal API implementations, generic implementations require explicit // knowledge of the entity and action (as well as $params). Bundle up these bits // into a convenient data structure. $result = $function($apiRequest); } elseif ($apiRequest['function'] && !$apiRequest['is_generic']) { _civicrm_api3_validate_fields($apiRequest['entity'], $apiRequest['action'], $apiRequest['params']); $result = isset($extra) ? $function($apiRequest['params'], $extra) : $function($apiRequest['params']); } else { return $errorFnName("API (" . $apiRequest['entity'] . "," . $apiRequest['action'] . ") does not exist (join the API team and implement it!)"); } foreach ($apiWrappers as $apiWrapper) { $result = $apiWrapper->toApiOutput($apiRequest, $result); } if (CRM_Utils_Array::value('format.is_success', $apiRequest['params']) == 1) { if ($result['is_error'] === 0) { return 1; } else { return 0; } } if (CRM_Utils_Array::value('format.only_id', $apiRequest['params']) && isset($result['id'])) { return $result['id']; } if (CRM_Utils_Array::value('is_error', $result, 0) == 0) { _civicrm_api_call_nested_api($apiRequest['params'], $result, $apiRequest['action'], $apiRequest['entity'], $apiRequest['version']); } if (CRM_Utils_Array::value('format.smarty', $apiRequest['params']) || CRM_Utils_Array::value('format_smarty', $apiRequest['params'])) { // return _civicrm_api_parse_result_through_smarty($result,$apiRequest['params']); } if (function_exists('xdebug_time_index') && CRM_Utils_Array::value('debug', $apiRequest['params']) && is_array($result)) { $result['xdebug']['peakMemory'] = xdebug_peak_memory_usage(); $result['xdebug']['memory'] = xdebug_memory_usage(); $result['xdebug']['timeIndex'] = xdebug_time_index(); } return $result; } catch (PEAR_Exception $e) { if (CRM_Utils_Array::value('format.is_success', $apiRequest['params']) == 1) { return 0; } $data = array(); $err = civicrm_api3_create_error($e->getMessage(), $data, $apiRequest); if (CRM_Utils_Array::value('debug', $apiRequest['params'])) { $err['trace'] = $e->getTraceSafe(); } else { $err['tip'] = "add debug=1 to your API call to have more info about the error"; } if (CRM_Utils_Array::value('is_transactional', $apiRequest)) { $tx->rollback(); } return $err; } catch (API_Exception $e) { if (!isset($apiRequest)) { $apiRequest = array(); } if (CRM_Utils_Array::value('format.is_success', CRM_Utils_Array::value('params', $apiRequest)) == 1) { return 0; } $data = $e->getExtraParams(); $err = civicrm_api3_create_error($e->getMessage(), $data, $apiRequest, $e->getCode()); if (CRM_Utils_Array::value('debug', CRM_Utils_Array::value('params', $apiRequest))) { $err['trace'] = $e->getTraceAsString(); } if (CRM_Utils_Array::value('is_transactional', CRM_Utils_Array::value('params', $apiRequest))) { $tx->rollback(); } return $err; } catch (Exception $e) { if (CRM_Utils_Array::value('format.is_success', $apiRequest['params']) == 1) { return 0; } $data = array(); $err = civicrm_api3_create_error($e->getMessage(), $data, $apiRequest, $e->getCode()); if (CRM_Utils_Array::value('debug', $apiRequest['params'])) { $err['trace'] = $e->getTraceAsString(); } if (CRM_Utils_Array::value('is_transactional', $apiRequest)) { $tx->rollback(); } return $err; } }