*
* @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
* @
*/
/**
* 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
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();
* 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){
/**
* 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);
* @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 = '') {
/**
* 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();
/**
* 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
*/
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
);
$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
/**
* 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) {
/**
* 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) {
$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');
}
}
}
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;
*
* @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);
}
/**
*
* @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'];
*
* @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(
*
* @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])) {
/**
* 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"));
* 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
_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)
);
}
/**
* 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);
/**
* 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) {
/**
* 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])) {