X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=api%2Fv3%2Futils.php;h=3fb9699444568f795502c324bd11f4cb4696ecae;hb=85bb0909cffd925f4cd5c0022a68a1ff74d3ad8e;hp=8d66ea0d116629c6b928cc95911341d3d56aa6a1;hpb=9537a4e194d806fe25f34de59ab46123a893ecf1;p=civicrm-core.git diff --git a/api/v3/utils.php b/api/v3/utils.php index 8d66ea0d11..3fb9699444 100644 --- a/api/v3/utils.php +++ b/api/v3/utils.php @@ -1,9 +1,9 @@ $data * @param array $data - * @param object $dao DAO / BAO object to be freed here * * @throws API_Exception * @return array */ -function civicrm_api3_create_error($msg, $data = array(), &$dao = NULL) { - //fix me - $dao should be param 4 & 3 should be $apiRequest - if (is_object($dao)) { - $dao->free(); - } - - if (is_array($dao)) { - if ($msg == 'DB Error: constraint violation' || substr($msg, 0,9) == 'DB Error:' || $msg == 'DB Error: already exists') { - try { - $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 civicrm_api3_create_error($msg, $data = array()) { $data['is_error'] = 1; $data['error_message'] = $msg; - // we will show sql to privelledged user only (not sure of a specific - // security hole here but seems sensible - perhaps should apply to the trace as well? + // we will show sql to privileged user only (not sure of a specific + // security hole here but seems sensible - perhaps should apply to the trace as well?) if(isset($data['sql']) && CRM_Core_Permission::check('Administer CiviCRM')) { - $data['debug_information'] = $data['sql']; - } - if (is_array($dao) && isset($dao['params']) && is_array($dao['params']) && !empty($dao['params']['api.has_parent'])) { - $errorCode = empty($data['error_code']) ? 'chained_api_failed' : $data['error_code']; - throw new API_Exception('Error in call to ' . $dao['entity'] . '_' . $dao['action'] . ' : ' . $msg, $errorCode, $data); + $data['debug_information'] = $data['sql']; // Isn't this redundant? + } else { + unset($data['sql']); } return $data; } @@ -170,7 +150,7 @@ function civicrm_api3_create_error($msg, $data = array(), &$dao = NULL) { /** * Format array in result output styple * - * @param array $values values generated by API operation (the result) + * @param array|int $values values generated by API operation (the result) * @param array $params parameters passed into API call * @param string $entity the entity being acted on * @param string $action the action passed to the API @@ -216,7 +196,7 @@ function civicrm_api3_create_success($values = 1, $params = array(), $entity = N $allFields = array_keys($apiFields['values']); } $paramFields = array_keys($params); - $undefined = array_diff($paramFields, $allFields, array_keys($_COOKIE), array('action', 'entity', 'debug', 'version', 'check_permissions', 'IDS_request_uri', 'IDS_user_agent', 'return', 'sequential', 'rowCount', 'option_offset', 'option_limit', 'custom', 'option_sort', 'options')); + $undefined = array_diff($paramFields, $allFields, array_keys($_COOKIE), array('action', 'entity', 'debug', 'version', 'check_permissions', 'IDS_request_uri', 'IDS_user_agent', 'return', 'sequential', 'rowCount', 'option_offset', 'option_limit', 'custom', 'option_sort', 'options', 'prettyprint')); if ($undefined) { $result['undefined_fields'] = array_merge($undefined); } @@ -253,7 +233,7 @@ function civicrm_api3_create_success($values = 1, $params = array(), $entity = N } if(!empty($params['options']['metadata'])) { // we've made metadata an array but only supporting 'fields' atm - if(in_array('fields', $params['options']['metadata'])) { + if(in_array('fields', (array) $params['options']['metadata'])) { $fields = civicrm_api3($entity, 'getfields', array('action' => substr($action, 0, 3) == 'get' ? 'get' : 'create')); $result['metadata']['fields'] = $fields['values']; } @@ -286,7 +266,7 @@ function _civicrm_api3_get_DAO($name) { // len ('civicrm_api3_') == 13 $name = substr($name, 13, $last - 13); } - + $name = _civicrm_api_get_camel_name($name, 3); if ($name == 'Individual' || $name == 'Household' || $name == 'Organization') { @@ -324,7 +304,10 @@ function _civicrm_api3_get_DAO($name) { } // Really weird apis can declare their own DAO name. Not sure if this is a good idea... - include_once "api/v3/$name.php"; + if(file_exists("api/v3/$name.php")) { + include_once "api/v3/$name.php"; + } + $daoFn = "_civicrm_api3_" . _civicrm_api_get_entity_name_from_camel($name) . "_DAO"; if (function_exists($daoFn)) { return $daoFn(); @@ -425,11 +408,10 @@ function _civicrm_api3_store_values(&$fields, &$params, &$values) { * 2 variants call * @param $entity * @param array $params as passed into api get or getcount function - * @param array $additional_options + * @param array $additional_options array of options (so we can modify the filter) * @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){ @@ -493,6 +475,7 @@ function _civicrm_api3_get_using_query_object($entity, $params, $additional_opti } } + $skipPermissions = !empty($params['check_permissions']) ? 0 :1; list($entities, $options) = CRM_Contact_BAO_Query::apiQuery( @@ -554,6 +537,13 @@ function _civicrm_api3_get_query_object($params, $mode, $entity) { /** * Function transfers the filters being passed into the DAO onto the params object + * @param CRM_Core_DAO $dao + * @param array $params + * @param bool $unique + * @param string $entity + * + * @throws API_Exception + * @throws Exception */ function _civicrm_api3_dao_set_filter(&$dao, $params, $unique = TRUE, $entity) { $entity = substr($dao->__table, 8); @@ -586,9 +576,6 @@ function _civicrm_api3_dao_set_filter(&$dao, $params, $unique = TRUE, $entity) { } } } - // http://issues.civicrm.org/jira/browse/CRM-9150 - stick with 'simple' operators for now - // support for other syntaxes is discussed in ticket but being put off for now - $acceptedSQLOperators = array('=', '<=', '>=', '>', '<', 'LIKE', "<>", "!=", "NOT LIKE", 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'); if (!$fields) { $fields = array(); } @@ -676,6 +663,7 @@ function _civicrm_api3_apply_filters_to_dao($filterField, $filterValue, &$dao) { * @param string $entity * @param string $action * + * @throws API_Exception * @return array $options options extracted from params */ function _civicrm_api3_get_options_from_params(&$params, $queryObject = FALSE, $entity = '', $action = '') { @@ -730,7 +718,7 @@ function _civicrm_api3_get_options_from_params(&$params, $queryObject = FALSE, $ '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, + 'return' => !empty($returnProperties) ? $returnProperties : array(), ); if ($options['sort'] && stristr($options['sort'], 'SELECT')) { @@ -811,6 +799,9 @@ function _civicrm_api3_build_fields_array(&$bao, $unique = TRUE) { /** * build fields array. This is the array of fields as it relates to the given DAO * returns unique fields as keys by default but if set but can return by DB fields + * @param CRM_Core_BAO $bao + * + * @return mixed */ function _civicrm_api3_get_unique_name_array(&$bao) { $fields = $bao->fields(); @@ -823,11 +814,13 @@ function _civicrm_api3_get_unique_name_array(&$bao) { /** * 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 * + * @param bool $autoFind + * * @return array * * @params array of arrays (key = id) of array of fields @@ -850,15 +843,6 @@ function _civicrm_api3_dao_to_array($dao, $params = NULL, $uniqueFields = TRUE, if(isset($dao->count)) { return $dao->count; } - //if custom fields are required we will endeavour to set them . NB passing $entity in might be a bit clunky / unrequired - if (!empty($entity) && !empty($params['return']) && is_array($params['return'])) { - foreach ($params['return'] as $return) { - if (substr($return, 0, 6) == 'custom') { - $custom = TRUE; - } - } - } - $fields = array_keys(_civicrm_api3_build_fields_array($dao, $uniqueFields)); @@ -873,7 +857,8 @@ function _civicrm_api3_dao_to_array($dao, $params = NULL, $uniqueFields = TRUE, } } $result[$dao->id] = $tmp; - if (!empty($custom)) { + + if(_civicrm_api3_custom_fields_are_required($entity, $params)) { _civicrm_api3_custom_data_get($result[$dao->id], $entity, $dao->id); } } @@ -882,6 +867,27 @@ function _civicrm_api3_dao_to_array($dao, $params = NULL, $uniqueFields = TRUE, return $result; } +/** + * We currently retrieve all custom fields or none at this level so if we know the entity + * && it can take custom fields & there is the string 'custom' in their return request we get them all, they are filtered on the way out + * @todo filter so only required fields are queried + * + * @param $params + * @param string $entity - entity name in CamelCase + * + * @return bool + */ +function _civicrm_api3_custom_fields_are_required($entity, $params) { + if (!array_key_exists($entity, CRM_Core_BAO_CustomQuery::$extendsMap)) { + return FALSE; + } + $options = _civicrm_api3_get_options_from_params($params); + //we check for possibility of 'custom' => 1 as well as specific custom fields + $returnString = implode('', $options['return']) . implode('', array_keys($options['return'])); + if(stristr($returnString, 'custom')) { + return TRUE; + } +} /** * Converts an object to an array * @@ -919,9 +925,25 @@ function _civicrm_api3_object_to_array_unique_fields(&$dao, &$values) { */ function _civicrm_api3_custom_format_params($params, &$values, $extends, $entityId = NULL) { $values['custom'] = array(); + $checkCheckBoxField = FALSE; + $entity = $extends; + if(in_array($extends, array('Household', 'Individual', 'Organization'))) { + $entity = 'Contact'; + } + + $fields = civicrm_api($entity, 'getfields', array('version' => 3, 'action' => 'create')); + if(!$fields['is_error']) { + // not sure if fields could be error - maybe change to using civicrm_api3 wrapper later - this is conservative + $fields = $fields['values']; + $checkCheckBoxField = TRUE; + } + foreach ($params as $key => $value) { list($customFieldID, $customValueID) = CRM_Core_BAO_CustomField::getKeyID($key, TRUE); if ($customFieldID && (!IS_NULL($value))) { + if ($checkCheckBoxField && !empty($fields['custom_' . $customFieldID]) && $fields['custom_' . $customFieldID]['html_type'] == 'CheckBox') { + formatCheckBoxField($value, 'custom_' . $customFieldID, $entity); + } CRM_Core_BAO_CustomField::formatCustomField($customFieldID, $values['custom'], $value, $extends, $customValueID, $entityId, FALSE, FALSE ); @@ -929,6 +951,117 @@ function _civicrm_api3_custom_format_params($params, &$values, $extends, $entity } } +/** + * @param $params + * @param $entity + */ +function _civicrm_api3_format_params_for_create(&$params, $entity) { + $nonGenericEntities = array('Contact', 'Individual', 'Household', 'Organization'); + + $customFieldEntities = array_diff_key(CRM_Core_BAO_CustomQuery::$extendsMap, array_fill_keys($nonGenericEntities, 1)); + if(!array_key_exists($entity, $customFieldEntities)) { + return; + } + $values = array(); + _civicrm_api3_custom_format_params($params, $values, $entity); + $params = array_merge($params, $values); +} + +/** + * we can't rely on downstream to add separators to checkboxes so we'll check here. We should look at pushing to BAO function + * and / or validate function but this is a safe place for now as it has massive test coverage & we can keep the change very specific + * note that this is specifically tested in the GRANT api test case so later refactoring should use that as a checking point + * + * We will only alter the value if we are sure that changing it will make it correct - if it appears wrong but does not appear to have a clear fix we + * don't touch - lots of very cautious code in here + * + * The resulting array should look like + * array( + * 'key' => 1, + * 'key1' => 1, + * ); + * + * OR one or more keys wrapped in a CRM_Core_DAO::VALUE_SEPARATOR - either it accepted by the receiving function + * + * @todo - we are probably skipping handling disabled options as presumably getoptions is not giving us them. This should be non-regressive but might + * be fixed in future + * + * @param $checkboxFieldValue + * @param $customFieldLabel + * @param $entity + * + */ +function formatCheckBoxField(&$checkboxFieldValue, $customFieldLabel, $entity) { + + if (is_string($checkboxFieldValue) && stristr($checkboxFieldValue, CRM_Core_DAO::VALUE_SEPARATOR)) { + // we can assume it's pre-formatted + return; + } + $options = civicrm_api($entity, 'getoptions', array('field' => $customFieldLabel, 'version' => 3)); + if (!empty($options['is_error'])) { + //the check is precautionary - can probably be removed later + return; + } + + $options = $options['values']; + $validValue = TRUE; + if (is_array($checkboxFieldValue)) { + foreach ($checkboxFieldValue as $key => $value) { + if (!array_key_exists($key, $options)) { + $validValue = FALSE; + } + } + if ($validValue) { + // we have been passed an array that is already in the 'odd' custom field format + return; + } + } + + // so we either have an array that is not keyed by the value or we have a string that doesn't hold separators + // if the array only has one item we'll treat it like any other string + if (is_array($checkboxFieldValue) && count($checkboxFieldValue) == 1) { + $possibleValue = reset($checkboxFieldValue); + } + if (is_string($checkboxFieldValue)) { + $possibleValue = $checkboxFieldValue; + } + if (isset($possibleValue) && array_key_exists($possibleValue, $options)) { + $checkboxFieldValue = CRM_Core_DAO::VALUE_SEPARATOR . $possibleValue . CRM_Core_DAO::VALUE_SEPARATOR; + return; + } + elseif (is_array($checkboxFieldValue)) { + // so this time around we are considering the values in the array + $possibleValues = $checkboxFieldValue; + $formatValue = TRUE; + } + elseif (stristr($checkboxFieldValue, ',')) { + $formatValue = TRUE; + //lets see if we should separate it - we do this near the end so we + // ensure we have already checked that the comma is not part of a legitimate match + // and of course, we don't make any changes if we don't now have matches + $possibleValues = explode(',', $checkboxFieldValue); + } + else { + // run out of ideas as to what the format might be - if it's a string it doesn't match with or without the ',' + return; + } + + foreach ($possibleValues as $index => $possibleValue) { + if (array_key_exists($possibleValue, $options)) { + // do nothing - we will leave formatValue set to true unless another value is not found (which would cause us to ignore the whole value set) + } + elseif (array_key_exists(trim($possibleValue), $options)) { + $possibleValues[$index] = trim($possibleValue); + } + else { + $formatValue = FALSE; + } + } + if ($formatValue) { + $checkboxFieldValue = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, $possibleValues) . CRM_Core_DAO::VALUE_SEPARATOR; + } +} + /** * @deprecated * This function ensures that we have the right input parameters @@ -995,48 +1128,6 @@ function _civicrm_api3_check_required_fields($params, $daoName, $return = FALSE) return TRUE; } -/** - * Check permissions for a given API call. - * - * @param $entity string API entity being accessed - * @param $action string API action being performed - * @param $params array params of the API call - * @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 early unless we’re told explicitly to do the permission check - if (empty($params['check_permissions']) or $params['check_permissions'] == FALSE) { - return TRUE; - } - - require_once 'CRM/Core/DAO/permissions.php'; - $permissions = _civicrm_api3_permissions($entity, $action, $params); - - // $params might’ve been reset by the alterAPIPermissions() hook - if (isset($params['check_permissions']) and $params['check_permissions'] == FALSE) { - return TRUE; - } - - 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; -} - /** * Function to do a 'standard' api get - when the api is only doing a $bao->find then use this * @@ -1054,19 +1145,22 @@ function _civicrm_api3_basic_get($bao_name, &$params, $returnAsSuccess = TRUE, $ return civicrm_api3_create_success(_civicrm_api3_dao_to_array($bao, $params, FALSE, $entity), $params, $entity, 'get'); } else { - return _civicrm_api3_dao_to_array($bao, $params, FALSE, $entity); + return _civicrm_api3_dao_to_array($bao, $params, FALSE, $entity, 'get'); } } /** * Function to do a 'standard' api create - when the api is only doing a $bao::create then use this + * * @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 + * + * @throws API_Exception * @return array */ function _civicrm_api3_basic_create($bao_name, &$params, $entity = NULL) { - + _civicrm_api3_format_params_for_create($params, $entity); $args = array(&$params); if (!empty($entity)) { $ids = array($entity => CRM_Utils_Array::value('id', $params)); @@ -1145,6 +1239,12 @@ function _civicrm_api3_basic_create_fallback($bao_name, &$params) { /** * Function to do a 'standard' api del - when the api is only doing a $bao::del then use this * if api::del doesn't exist it will try DAO delete method + * + * @param $bao_name + * @param $params + * + * @return array API result array + * @throws API_Exception */ function _civicrm_api3_basic_delete($bao_name, &$params) { @@ -1186,7 +1286,7 @@ function _civicrm_api3_basic_delete($bao_name, &$params) { * @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, + $groupTree = CRM_Core_BAO_CustomGroup::getTree($entity, CRM_Core_DAO::$_nullObject, $entity_id, $groupID, @@ -1196,19 +1296,23 @@ function _civicrm_api3_custom_data_get(&$returnArray, $entity, $entity_id, $grou $groupTree = CRM_Core_BAO_CustomGroup::formatGroupTree($groupTree, 1, CRM_Core_DAO::$_nullObject); $customValues = array(); CRM_Core_BAO_CustomGroup::setDefaults($groupTree, $customValues); + $fieldInfo = array(); + foreach ($groupTree as $set) { + $fieldInfo += $set['fields']; + } if (!empty($customValues)) { foreach ($customValues as $key => $val) { - if (strstr($key, '_id')) { - $idkey = substr($key, 0, -3); - $returnArray['custom_' . (CRM_Core_BAO_CustomField::getKeyID($idkey) . "_id")] = $val; - $returnArray[$key] = $val; - } - else { - // per standard - return custom_fieldID - $returnArray['custom_' . (CRM_Core_BAO_CustomField::getKeyID($key))] = $val; + // per standard - return custom_fieldID + $id = CRM_Core_BAO_CustomField::getKeyID($key); + $returnArray['custom_' . $id] = $val; + + //not standard - but some api did this so guess we should keep - cheap as chips + $returnArray[$key] = $val; - //not standard - but some api did this so guess we should keep - cheap as chips - $returnArray[$key] = $val; + // Shim to restore legacy behavior of ContactReference custom fields + if (!empty($fieldInfo[$id]) && $fieldInfo[$id]['data_type'] == 'ContactReference') { + $returnArray['custom_' . $id . '_id'] = $returnArray[$key . '_id'] = $val; + $returnArray['custom_' . $id] = $returnArray[$key] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $val, 'sort_name'); } } } @@ -1238,6 +1342,7 @@ function _civicrm_api3_validate_fields($entity, $action, &$params, $fields, $err case 4: case 12: + case CRM_Utils_Type::T_TIMESTAMP: //field is of type date or datetime _civicrm_api3_validate_date($params, $fieldName, $fieldInfo); break; @@ -1286,26 +1391,45 @@ function _civicrm_api3_validate_fields($entity, $action, &$params, $fields, $err * * @param array $params params from civicrm_api * @param string $fieldName uniquename of field being checked - * @param $fieldInfo + * @param array $fieldInfo array of fields from getfields function * @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? if (!empty($params[$fieldInfo['name']])) { - //accept 'whatever strtotime accepts - if (strtotime($params[$fieldInfo['name']]) === FALSE) { - throw new Exception($fieldInfo['name'] . " is not a valid date: " . $params[$fieldInfo['name']]); - } - $params[$fieldInfo['name']] = CRM_Utils_Date::processDate($params[$fieldInfo['name']]); + $params[$fieldInfo['name']] = _civicrm_api3_getValidDate($params[$fieldInfo['name']], $fieldInfo['name'], $fieldInfo['type']); } if ((CRM_Utils_Array::value('name', $fieldInfo) != $fieldName) && !empty($params[$fieldName])) { - //If the unique field name differs from the db name & is set handle it here - if (strtotime($params[$fieldName]) === FALSE) { - throw new Exception($fieldName . " is not a valid date: " . $params[$fieldName]); + $params[$fieldName] = _civicrm_api3_getValidDate($params[$fieldName], $fieldName, $fieldInfo['type']); + } +} + +/** + * convert date into BAO friendly date + * we accept 'whatever strtotime accepts' + * + * @param string $dateValue + * @param $fieldName + * @param $fieldType + * + * @throws Exception + * @internal param $fieldInfo + * + * @internal param $params + * @return mixed + */ +function _civicrm_api3_getValidDate($dateValue, $fieldName, $fieldType) { + if (is_array($dateValue)) { + foreach ($dateValue as $key => $value) { + $dateValue[$key] = _civicrm_api3_getValidDate($value, $fieldName, $fieldType); } - $params[$fieldName] = CRM_Utils_Date::processDate($params[$fieldName]); + return $dateValue; + } + if (strtotime($dateValue) === FALSE) { + throw new Exception($fieldName . " is not a valid date: " . $dateValue); } + $format = ($fieldType == CRM_Utils_Type::T_DATE) ? 'Ymd000000' : 'YmdHis'; + return CRM_Utils_Date::processDate($dateValue, NULL, FALSE, $format); } /** @@ -1313,9 +1437,8 @@ function _civicrm_api3_validate_date(&$params, &$fieldName, &$fieldInfo) { * * @param array $params params from civicrm_api * @param string $fieldName uniquename of field being checked - * @param $fieldInfo + * @param array $fieldInfo array of fields from getfields function * @throws Exception - * @internal param array $fieldinfo array of fields from getfields function */ function _civicrm_api3_validate_constraint(&$params, &$fieldName, &$fieldInfo) { $dao = new $fieldInfo['FKClassName']; @@ -1332,9 +1455,8 @@ function _civicrm_api3_validate_constraint(&$params, &$fieldName, &$fieldInfo) { * * @param array $params params from civicrm_api * @param string $fieldName uniquename of field being checked - * @param $fieldInfo + * @param $fieldInfo array of fields from getfields function * @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( @@ -1344,7 +1466,7 @@ function _civicrm_api3_validate_uniquekey(&$params, &$fieldName, &$fieldInfo) { // an entry already exists for this unique field if ($existing['count'] == 1) { // question - could this ever be a security issue? - throw new Exception("Field: `$fieldName` must be unique. An conflicting entity already exists - id: " . $existing['id']); + throw new API_Exception("Field: `$fieldName` must be unique. An conflicting entity already exists - id: " . $existing['id']); } } @@ -1506,23 +1628,17 @@ function _civicrm_api_get_custom_fields($entity, &$params) { FALSE, FALSE ); - // find out if we have any requests to resolve options - $getoptions = CRM_Utils_Array::value('get_options', CRM_Utils_Array::value('options',$params)); - if(!is_array($getoptions)){ - $getoptions = array($getoptions); - } + + $ret = array(); foreach ($customfields as $key => $value) { // Regular fields have a 'name' property $value['name'] = 'custom_' . $key; + $value['title'] = $value['label']; $value['type'] = _getStandardTypeFromCustomDataType($value['data_type']); - $customfields['custom_' . $key] = $value; - if (in_array('custom_' . $key, $getoptions)) { - $customfields['custom_' . $key]['options'] = CRM_Core_BAO_CustomOption::valuesByID($key); - } - unset($customfields[$key]); + $ret['custom_' . $key] = $value; } - return $customfields; + return $ret; } /** * Translate the custom field data_type attribute into a std 'type' @@ -1544,33 +1660,7 @@ function _getStandardTypeFromCustomDataType($dataType) { ); return $mapping[$dataType]; } -/** - * Return array of defaults for the given API (function is a wrapper on getfields) - */ -function _civicrm_api3_getdefaults($apiRequest, $fields) { - $defaults = array(); - - foreach ($fields as $field => $values) { - if (isset($values['api.default'])) { - $defaults[$field] = $values['api.default']; - } - } - return $defaults; -} - -/** - * Return array of defaults for the given API (function is a wrapper on getfields) - */ -function _civicrm_api3_getrequired($apiRequest, $fields) { - $required = array('version'); - foreach ($fields as $field => $values) { - if (!empty($values['api.required'])) { - $required[] = $field; - } - } - return $required; -} /** * Fill params array with alternate (alias) values where a field has an alias and that is filled & the main field isn't @@ -1619,13 +1709,11 @@ function _civicrm_api3_swap_out_aliases(&$apiRequest, $fields) { * * @param array $params params from civicrm_api * @param string $fieldName uniquename of field being checked - * @param $fieldInfo - * @param $entity + * @param array $fieldInfo array of fields from getfields function + * @param string $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 if (!empty($params[$fieldName])) { // if value = 'user_contact_id' (or similar), replace value with contact id if (!is_numeric($params[$fieldName]) && is_scalar($params[$fieldName])) { @@ -1666,12 +1754,9 @@ function _civicrm_api3_validate_integer(&$params, &$fieldName, &$fieldInfo, $ent function _civicrm_api3_resolve_contactID($contactIdExpr) { //if value = 'user_contact_id' replace value with logged in user id if ($contactIdExpr == "user_contact_id") { - $session = &CRM_Core_Session::singleton(); - if (!is_numeric($session->get('userID'))) { - return NULL; - } - return $session->get('userID'); - } elseif (preg_match('/^@user:(.*)$/', $contactIdExpr, $matches)) { + return CRM_Core_Session::getLoggedInContactID(); + } + elseif (preg_match('/^@user:(.*)$/', $contactIdExpr, $matches)) { $config = CRM_Core_Config::singleton(); $ufID = $config->userSystem->getUfId($matches[1]); @@ -1691,13 +1776,13 @@ function _civicrm_api3_resolve_contactID($contactIdExpr) { /** * Validate html (check for scripting attack) - * @param $params - * @param $fieldName - * @param $fieldInfo + * @param array $params + * @param string $fieldName + * @param array $fieldInfo * * @throws API_Exception */ -function _civicrm_api3_validate_html(&$params, &$fieldName, &$fieldInfo) { +function _civicrm_api3_validate_html(&$params, &$fieldName, $fieldInfo) { if ($value = CRM_Utils_Array::value($fieldName, $params)) { if (!CRM_Utils_Rule::xssString($value)) { throw new API_Exception('Illegal characters in input (potential scripting attack)', array("field"=>$fieldName,"error_code"=>"xss")); @@ -1709,11 +1794,10 @@ function _civicrm_api3_validate_html(&$params, &$fieldName, &$fieldInfo) { * Validate string fields being passed into API. * @param array $params params from civicrm_api * @param string $fieldName uniquename of field being checked - * @param $fieldInfo - * @param $entity + * @param array $fieldInfo array of fields from getfields function + * @param string $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 @@ -1739,8 +1823,8 @@ function _civicrm_api3_validate_string(&$params, &$fieldName, &$fieldInfo, $enti _civicrm_api3_api_match_pseudoconstant($params, $entity, $fieldName, $fieldInfo); } // Check our field length - elseif (is_string($value) && !empty($fieldInfo['maxlength']) && strlen($value) > $fieldInfo['maxlength']) { - throw new API_Exception("Value for $fieldName is " . strlen($value) . " characters - This field has a maxlength of {$fieldInfo['maxlength']} characters.", + elseif (is_string($value) && !empty($fieldInfo['maxlength']) && strlen(utf8_decode($value)) > $fieldInfo['maxlength']) { + throw new API_Exception("Value for $fieldName is " . strlen(utf8_decode($value)) . " characters - This field has a maxlength of {$fieldInfo['maxlength']} characters.", 2100, array('field' => $fieldName) ); } @@ -1750,10 +1834,10 @@ function _civicrm_api3_validate_string(&$params, &$fieldName, &$fieldInfo, $enti /** * Validate & swap out any pseudoconstants / options * - * @param $params: api parameters - * @param $entity: api entity name - * @param $fieldName: field name used in api call (not necessarily the canonical name) - * @param $fieldInfo: getfields meta-data + * @param array $params: api parameters + * @param string $entity: api entity name + * @param string $fieldName: field name used in api call (not necessarily the canonical name) + * @param array $fieldInfo: getfields meta-data */ function _civicrm_api3_api_match_pseudoconstant(&$params, $entity, $fieldName, $fieldInfo) { $options = CRM_Utils_Array::value('options', $fieldInfo); @@ -1794,9 +1878,9 @@ function _civicrm_api3_api_match_pseudoconstant(&$params, $entity, $fieldName, $ /** * Validate & swap a single option value for a field * - * @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) + * @param string $value: field value + * @param array $options: array of options for this field + * @param string $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) { @@ -1823,10 +1907,11 @@ function _civicrm_api3_api_match_pseudoconstant_value(&$value, $options, $fieldN /** * Returns the canonical name of a field - * @param $entity: api entity name (string should already be standardized - no camelCase) - * @param $fieldName: any variation of a field's name (name, unique_name, api.alias) * - * @return (string|bool) fieldName or FALSE if the field does not exist + * @param $entity : api entity name (string should already be standardized - no camelCase) + * @param $fieldName : any variation of a field's name (name, unique_name, api.alias) + * + * @return bool|string (string|bool) fieldName or FALSE if the field does not exist */ function _civicrm_api3_api_resolve_alias($entity, $fieldName) { if (strpos($fieldName, 'custom_') === 0 && is_numeric($fieldName[7])) {