*
* @param array $params array of fields to check
* @param array $daoName string DAO to check for required fields (create functions only)
- * @param array $keys list of required fields options. One of the options is required
+ * @param array $keyoptions
+ *
+ * @internal param array $keys list of required fields options. One of the options is required
* @return null or throws error if there the required fields not present
-
* @
- *
*/
function civicrm_api3_verify_one_mandatory($params, $daoName = NULL, $keyoptions = array(
)) {
* @param array $keys list of required fields. A value can be an array denoting that either this or that is required.
* @param bool $verifyDAO
*
+ * @throws API_Exception
* @return null or throws error if there the required fields not present
*
* @todo see notes on _civicrm_api3_check_required_fields regarding removing $daoName param
/**
*
- * @param <type> $msg
* @param <type> $data
+ * @param array $data
* @param object $dao DAO / BAO object to be freed here
*
- * @return <type>
+ * @throws API_Exception
+ * @return array <type>
*/
function civicrm_api3_create_error($msg, $data = array(), &$dao = NULL) {
//fix me - $dao should be param 4 & 3 should be $apiRequest
if (is_array($dao)) {
if ($msg == 'DB Error: constraint violation' || substr($msg, 0,9) == 'DB Error:' || $msg == 'DB Error: already exists') {
try {
- _civicrm_api3_validate_fields($dao['entity'], $dao['action'], $dao['params'], TRUE);
+ $fields = _civicrm_api3_api_getfields($dao);
+ _civicrm_api3_validate_fields($dao['entity'], $dao['action'], $dao['params'], $fields, TRUE);
}
catch(Exception $e) {
$msg = $e->getMessage();
/**
* Function to return the DAO of the function or Entity
- * @param $name is either a function of the api (civicrm_{entity}_create or the entity name
+ * @param String $name either a function of the api (civicrm_{entity}_create or the entity name
* return the DAO name to manipulate this function
* eg. "civicrm_api3_contact_create" or "Contact" will return "CRM_Contact_BAO_Contact"
+ * @return mixed|string
*/
function _civicrm_api3_get_DAO($name) {
if (strpos($name, 'civicrm_api3') !== FALSE) {
/**
* Function to return the DAO of the function or Entity
- * @param $name is either a function of the api (civicrm_{entity}_create or the entity name
+ * @param String $name is either a function of the api (civicrm_{entity}_create or the entity name
* return the DAO name to manipulate this function
* eg. "civicrm_contact_create" or "Contact" will return "CRM_Contact_BAO_Contact"
+ * @return mixed
*/
function _civicrm_api3_get_BAO($name) {
$dao = _civicrm_api3_get_DAO($name);
- $dao = str_replace("DAO", "BAO", $dao);
- return $dao;
+ if (!$dao) {
+ return NULL;
+ }
+ $bao = str_replace("DAO", "BAO", $dao);
+ $file = strtr($bao, '_', '/') . '.php';
+ // Check if this entity actually has a BAO. Fall back on the DAO if not.
+ return stream_resolve_include_path($file) ? $bao : $dao;
}
/**
}
return $valueFound;
}
+
/**
* The API supports 2 types of get requestion. The more complex uses the BAO query object.
* This is a generic function for those functions that call it
* others that use the query object. Note that this function passes permission information in.
* The others don't
*
+ * @param $entity
* @param array $params as passed into api get or getcount function
- * @param array $options array of options (so we can modify the filter)
+ * @param array $additional_options
* @param bool $getCount are we just after the count
+ *
+ * @return
+ * @internal param array $options array of options (so we can modify the filter)
*/
function _civicrm_api3_get_using_query_object($entity, $params, $additional_options = array(), $getCount = NULL){
/**
* Get sort, limit etc options from the params - supporting old & new formats.
* get returnproperties for legacy
+ *
* @param array $params params array as passed into civicrm_api
* @param bool $queryObject - is this supporting a queryobject api (e.g contact) - if so we support more options
* for legacy report & return a unique fields array
+ *
+ * @param string $entity
+ * @param string $action
+ *
* @return array $options options extracted from params
*/
function _civicrm_api3_get_options_from_params(&$params, $queryObject = FALSE, $entity = '', $action = '') {
$returnProperties = array_fill_keys($returnProperties, 1);
}
}
- if($entity && $action =='get' ){
- if(CRM_Utils_Array::value('id',$returnProperties)){
+ if ($entity && $action =='get') {
+ if (CRM_Utils_Array::value('id',$returnProperties)) {
$returnProperties[$entity . '_id'] = 1;
unset($returnProperties['id']);
}
}
}
-
$options = array(
- 'offset' => $offset,
- 'sort' => $sort,
- 'limit' => $limit,
+ 'offset' => CRM_Utils_Rule::integer($offset) ? $offset : NULL,
+ 'sort' => CRM_Utils_Rule::string($sort) ? $sort : NULL,
+ 'limit' => CRM_Utils_Rule::integer($limit) ? $limit : NULL,
'is_count' => $is_count,
'return' => !empty($returnProperties) ? $returnProperties : NULL,
);
+ if ($options['sort'] && stristr($options['sort'], 'SELECT')) {
+ throw new API_Exception('invalid string in sort options');
+ }
+
if (!$queryObject) {
return $options;
}
if (substr($n, 0, 7) == 'return.') {
$legacyreturnProperties[substr($n, 7)] = $v;
}
- elseif($n == 'id'){
+ elseif ($n == 'id') {
$inputParams[$entity. '_id'] = $v;
}
elseif (in_array($n, $otherVars)) {}
- else{
+ else {
$inputParams[$n] = $v;
+ if ($v && !is_array($v) && stristr($v, 'SELECT')) {
+ throw new API_Exception('invalid string');
+ }
}
}
$options['return'] = array_merge($returnProperties, $legacyreturnProperties);
/**
* Apply options (e.g. sort, limit, order by) to DAO object (prior to find)
+ *
* @param array $params params array as passed into civicrm_api
* @param object $dao DAO object
+ * @param $entity
*/
function _civicrm_api3_apply_options_to_dao(&$params, &$dao, $entity) {
/**
* Converts an DAO object to an array
*
- * @param object $dao (reference )object to convert
+ * @param object $dao (reference )object to convert
+ * @param null $params
+ * @param bool $uniqueFields
+ * @param string $entity
+ *
+ * @return array
+ *
* @params array of arrays (key = id) of array of fields
+ *
* @static void
* @access public
*/
/**
* Converts an object to an array
*
- * @param object $dao (reference) object to convert
- * @param array $values (reference) array
- * @param array $uniqueFields
+ * @param object $dao (reference) object to convert
+ * @param array $values (reference) array
+ * @param array|bool $uniqueFields
*
* @return array
* @static void
* api level. Hence the intention is to remove this function
* & the associated param from viery_mandatory
*
- * @param array $params Associative array of property name/value
+ * @param array $params Associative array of property name/value
* pairs to insert in new history.
+ * @param $daoName
+ * @param bool $return
+ *
* @daoName string DAO to check params agains
*
* @return bool should the missing fields be returned as an array (core error created as default)
* @param $entity string API entity being accessed
* @param $action string API action being performed
* @param $params array params of the API call
- * @param $throw bool whether to throw exception instead of returning false
+ * @param $throw deprecated bool whether to throw exception instead of returning false
*
+ * @throws Exception
* @return bool whether the current API user has the permission to make the call
*/
function _civicrm_api3_api_check_permission($entity, $action, &$params, $throw = TRUE) {
return TRUE;
}
- foreach ($permissions as $perm) {
- if (!CRM_Core_Permission::check($perm)) {
- if ($throw) {
- throw new Exception("API permission check failed for $entity/$action call; missing permission: $perm.");
- }
- else {
- return FALSE;
+ if (!CRM_Core_Permission::check($permissions)) {
+ if ($throw) {
+ if(is_array($permissions)) {
+ $permissions = implode(' and ', $permissions);
}
+ throw new Exception("API permission check failed for $entity/$action call; insufficient permission: require $permissions");
+ }
+ else {
+ //@todo remove this - this is an internal api function called with $throw set to TRUE. It is only called with false
+ // in tests & that should be tidied up
+ return FALSE;
}
}
+
return TRUE;
}
* @param string $bao_name name of BAO
* @param array $params params from api
* @param bool $returnAsSuccess return in api success format
+ * @param string $entity
+ *
+ * @return array
*/
function _civicrm_api3_basic_get($bao_name, &$params, $returnAsSuccess = TRUE, $entity = "") {
$bao = new $bao_name();
* @param string $bao_name Name of BAO Class
* @param array $params parameters passed into the api call
* @param string $entity Entity - pass in if entity is non-standard & required $ids array
+ * @return array
*/
function _civicrm_api3_basic_create($bao_name, &$params, $entity = NULL) {
if (is_null($bao)) {
return civicrm_api3_create_error('Entity not created (' . $fct_name . ')');
}
+ elseif (is_a($bao, 'CRM_Core_Error')) {
+ //some wierd circular thing means the error takes itself as an argument
+ $msg = $bao->getMessages($bao);
+ // the api deals with entities on a one-by-one basis. However, the contribution bao pushes entities
+ // onto the error object - presumably because the contribution import is not handling multiple errors correctly
+ // so we need to reset the error object here to avoid getting concatenated errors
+ //@todo - the mulitple error handling should be moved out of the contribution object to the import / multiple entity processes
+ CRM_Core_Error::singleton()->reset();
+ throw new API_Exception($msg);
+ }
else {
$values = array();
_civicrm_api3_object_to_array($bao, $values[$bao->id]);
/**
* For BAO's which don't have a create() or add() functions, use this fallback implementation.
*
- * FIXME There's an intuitive sense that this behavior should be defined somehow in the BAO/DAO class
+ * @fixme There's an intuitive sense that this behavior should be defined somehow in the BAO/DAO class
* structure. In practice, that requires a fair amount of refactoring and/or kludgery.
*
* @param string $bao_name
* @param array $params
+ *
+ * @throws API_Exception
* @return CRM_Core_DAO|NULL an instance of the BAO
*/
function _civicrm_api3_basic_create_fallback($bao_name, &$params) {
*
* @param array $returnArray - array to append custom data too - generally $result[4] where 4 is the entity id.
* @param string $entity e.g membership, event
+ * @param $entity_id
* @param int $groupID - per CRM_Core_BAO_CustomGroup::getTree
* @param int $subType e.g. membership_type_id where custom data doesn't apply to all membership types
* @param string $subName - Subtype of entity
- *
*/
function _civicrm_api3_custom_data_get(&$returnArray, $entity, $entity_id, $groupID = NULL, $subType = NULL, $subName = NULL) {
$groupTree = &CRM_Core_BAO_CustomGroup::getTree($entity,
* @param string $entity
* @param string $action
* @param array $params -
- * all variables are the same as per civicrm_api
+ * @param array $fields response from getfields all variables are the same as per civicrm_api
+ * @param bool $errorMode errorMode do intensive post fail checks?
+ * @throws Exception
*/
-function _civicrm_api3_validate_fields($entity, $action, &$params, $errorMode = NULL) {
- //skip any entities without working getfields functions
- $skippedEntities = array('entity', 'mailinggroup', 'customvalue', 'custom_value', 'mailing_group');
- if (in_array(strtolower($entity), $skippedEntities) || strtolower($action) == 'getfields') {
- return;
- }
- $fields = civicrm_api($entity, 'getfields', array('version' => 3, 'action' => $action));
- $fields = array_intersect_key($fields['values'], $params);
+function _civicrm_api3_validate_fields($entity, $action, &$params, $fields, $errorMode = False) {
+ $fields = array_intersect_key($fields, $params);
foreach ($fields as $fieldName => $fieldInfo) {
switch (CRM_Utils_Array::value('type', $fieldInfo)) {
case CRM_Utils_Type::T_INT:
*
* @param array $params params from civicrm_api
* @param string $fieldName uniquename of field being checked
- * @param array $fieldinfo array of fields from getfields function
+ * @param $fieldInfo
+ * @throws Exception
+ * @internal param array $fieldinfo array of fields from getfields function
*/
function _civicrm_api3_validate_date(&$params, &$fieldName, &$fieldInfo) {
//should we check first to prevent it from being copied if they have passed in sql friendly format?
*
* @param array $params params from civicrm_api
* @param string $fieldName uniquename of field being checked
- * @param array $fieldinfo array of fields from getfields function
+ * @param $fieldInfo
+ * @throws Exception
+ * @internal param array $fieldinfo array of fields from getfields function
*/
function _civicrm_api3_validate_constraint(&$params, &$fieldName, &$fieldInfo) {
$dao = new $fieldInfo['FKClassName'];
*
* @param array $params params from civicrm_api
* @param string $fieldName uniquename of field being checked
- * @param array $fieldinfo array of fields from getfields function
+ * @param $fieldInfo
+ * @throws Exception
+ * @internal param array $fieldinfo array of fields from getfields function
*/
function _civicrm_api3_validate_uniquekey(&$params, &$fieldName, &$fieldInfo) {
$existing = civicrm_api($params['entity'], 'get', array(
* @param array $params params from civicrm_api, including:
* - 'values': an array of records to save
* - all other items: keys which identify new/pre-existing records
+ * @return array|int
*/
function _civicrm_api3_generic_replace($entity, $params) {
}
// Extract the keys -- somewhat scary, don't think too hard about it
- $baseParams = $params;
- unset($baseParams['values']);
- unset($baseParams['sequential']);
+ $baseParams = _civicrm_api3_generic_replace_base_params($params);
// Lookup pre-existing records
$preexisting = civicrm_api($entity, 'get', $baseParams, $params);
}
}
+/**
+ * @param $params
+ *
+ * @return mixed
+ */
+function _civicrm_api3_generic_replace_base_params($params) {
+ $baseParams = $params;
+ unset($baseParams['values']);
+ unset($baseParams['sequential']);
+ unset($baseParams['options']);
+ return $baseParams;
+}
+
/**
* returns fields allowable by api
+ *
* @param $entity string Entity to query
* @param bool $unique index by unique fields?
+ * @param array $params
+ *
+ * @return array
*/
function _civicrm_api_get_fields($entity, $unique = FALSE, &$params = array(
)) {
$customfields = array();
$entity = _civicrm_api_get_camel_name($entity);
if (strtolower($entity) == 'contact') {
+ // Use sub-type if available, otherwise stick with 'Contact'
$entity = CRM_Utils_Array::value('contact_type', $params);
}
$retrieveOnlyParent = FALSE;
/**
* Return array of defaults for the given API (function is a wrapper on getfields)
*/
-function _civicrm_api3_getdefaults($apiRequest) {
+function _civicrm_api3_getdefaults($apiRequest, $fields) {
$defaults = array();
- $result = civicrm_api3($apiRequest['entity'],
- 'getfields',
- array(
- 'action' => $apiRequest['action'],
- )
- );
-
- foreach ($result['values'] as $field => $values) {
+ foreach ($fields as $field => $values) {
if (isset($values['api.default'])) {
$defaults[$field] = $values['api.default'];
}
/**
* Return array of defaults for the given API (function is a wrapper on getfields)
*/
-function _civicrm_api3_getrequired($apiRequest) {
+function _civicrm_api3_getrequired($apiRequest, $fields) {
$required = array('version');
- $result = civicrm_api($apiRequest['entity'],
- 'getfields',
- array(
- 'version' => 3,
- 'action' => $apiRequest['action'],
- )
- );
- foreach ($result['values'] as $field => $values) {
+ foreach ($fields as $field => $values) {
if (CRM_Utils_Array::value('api.required', $values)) {
$required[] = $field;
}
*
* Function also swaps unique fields for non-unique fields & vice versa.
*/
-function _civicrm_api3_swap_out_aliases(&$apiRequest) {
- if (strtolower($apiRequest['action'] == 'getfields')) {
- if (CRM_Utils_Array::value('api_action', $apiRequest['params'])) {
- $apiRequest['params']['action'] = $apiRequest['params']['api_action'];
- unset($apiRequest['params']['api_action']);
- }
- return;
- }
- $result = civicrm_api3($apiRequest['entity'],
- 'getfields',
- array(
- 'action' => $apiRequest['action'],
- )
- );
-
- foreach ($result['values'] as $field => $values) {
+function _civicrm_api3_swap_out_aliases(&$apiRequest, $fields) {
+ foreach ($fields as $field => $values) {
$uniqueName = CRM_Utils_Array::value('uniqueName', $values);
if (CRM_Utils_Array::value('api.aliases', $values)) {
// if aliased field is not set we try to use field alias
*
* @param array $params params from civicrm_api
* @param string $fieldName uniquename of field being checked
- * @param array $fieldinfo array of fields from getfields function
+ * @param $fieldInfo
+ * @param $entity
+ * @throws API_Exception
+ * @internal param array $fieldinfo array of fields from getfields function
*/
function _civicrm_api3_validate_integer(&$params, &$fieldName, &$fieldInfo, $entity) {
//if fieldname exists in params
return NULL;
}
+/**
+ * Validate html (check for scripting attack)
+ * @param $params
+ * @param $fieldName
+ * @param $fieldInfo
+ *
+ * @throws API_Exception
+ */
function _civicrm_api3_validate_html(&$params, &$fieldName, &$fieldInfo) {
if ($value = CRM_Utils_Array::value($fieldName, $params)) {
if (!CRM_Utils_Rule::xssString($value)) {
* Validate string fields being passed into API.
* @param array $params params from civicrm_api
* @param string $fieldName uniquename of field being checked
- * @param array $fieldinfo array of fields from getfields function
+ * @param $fieldInfo
+ * @param $entity
+ * @throws API_Exception
+ * @throws Exception
+ * @internal param array $fieldinfo array of fields from getfields function
*/
function _civicrm_api3_validate_string(&$params, &$fieldName, &$fieldInfo, $entity) {
// If fieldname exists in params
function _civicrm_api3_api_match_pseudoconstant(&$params, $entity, $fieldName, $fieldInfo) {
$options = CRM_Utils_Array::value('options', $fieldInfo);
if (!$options) {
+ if(strtolower($entity) == 'profile' && !empty($fieldInfo['entity'])) {
+ // we need to get the options from the entity the field relates to
+ $entity = $fieldInfo['entity'];
+ }
$options = civicrm_api($entity, 'getoptions', array('version' => 3, 'field' => $fieldInfo['name'], 'context' => 'validate'));
$options = CRM_Utils_Array::value('values', $options, array());
}
- // If passed a value-seperated string, explode to an array, then re-implode after matching values
+ // If passed a value-separated string, explode to an array, then re-implode after matching values
$implode = FALSE;
if (is_string($params[$fieldName]) && strpos($params[$fieldName], CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) {
$params[$fieldName] = CRM_Utils_Array::explodePadded($params[$fieldName]);
* @param $value: field value
* @param $options: array of options for this field
* @param $fieldName: field name used in api call (not necessarily the canonical name)
+ * @throws API_Exception
*/
function _civicrm_api3_api_match_pseudoconstant_value(&$value, $options, $fieldName) {
// If option is a key, no need to translate