X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=api%2Fv3%2Futils.php;h=3fb9699444568f795502c324bd11f4cb4696ecae;hb=85bb0909cffd925f4cd5c0022a68a1ff74d3ad8e;hp=8acc21dfc5b460772846a5ae125fcde74b88a05c;hpb=3df82588c25bf178ff9a550b35b65ed2dfc1c646;p=civicrm-core.git diff --git a/api/v3/utils.php b/api/v3/utils.php index 8acc21dfc5..3fb9699444 100644 --- a/api/v3/utils.php +++ b/api/v3/utils.php @@ -50,9 +50,8 @@ function _civicrm_api3_initialize() { * * @param array $params array of fields to checkl * @param array $daoName string DAO to check for required fields (create functions only) - * @param array $keyoptions + * @param array $keyoptions list of required fields options. One of the options is required * - * @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 * @ */ @@ -151,7 +150,7 @@ function civicrm_api3_create_error($msg, $data = array()) { /** * 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 @@ -308,6 +307,7 @@ function _civicrm_api3_get_DAO($name) { 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(); @@ -408,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){ @@ -538,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); @@ -657,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 = '') { @@ -792,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(); @@ -915,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 ); @@ -941,6 +967,101 @@ function _civicrm_api3_format_params_for_create(&$params, $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 @@ -1030,9 +1151,12 @@ function _civicrm_api3_basic_get($bao_name, &$params, $returnAsSuccess = TRUE, $ /** * 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) { @@ -1115,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) { @@ -1166,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'); } } } @@ -1208,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; @@ -1256,27 +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']]); - } - $format = ($fieldInfo['type'] == CRM_Utils_Type::T_DATE) ? 'Ymd000000' : 'YmdHis'; - $params[$fieldInfo['name']] = CRM_Utils_Date::processDate($params[$fieldInfo['name']], NULL, FALSE, $format); + $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); } /** @@ -1284,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']; @@ -1303,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( @@ -1558,10 +1709,9 @@ 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 (!empty($params[$fieldName])) { @@ -1626,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")); @@ -1644,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 @@ -1674,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) ); } @@ -1685,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); @@ -1729,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) { @@ -1758,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])) {