X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=api%2Fv3%2Futils.php;h=849f70677d1815148391e268c2fcd1aa3470f058;hb=495227621745a89a81eacf23842bc5ffd2510ba6;hp=dea9755a4b496d292d8ed95c1a2773f10b9cfb29;hpb=9ce7a63e2d59890ffe6f660d1935cd5cb5263e2d;p=civicrm-core.git diff --git a/api/v3/utils.php b/api/v3/utils.php index dea9755a4b..849f70677d 100644 --- a/api/v3/utils.php +++ b/api/v3/utils.php @@ -48,7 +48,7 @@ function _civicrm_api3_initialize() { /** * Wrapper Function for civicrm_verify_mandatory to make it simple to pass either / or fields for checking * - * @param array $params array of fields to check + * @param array $params array of fields to checkl * @param array $daoName string DAO to check for required fields (create functions only) * @param array $keyoptions * @@ -116,7 +116,9 @@ function civicrm_api3_verify_mandatory($params, $daoName = NULL, $keys = array( } } else { - if (!array_key_exists($key, $params) || empty($params[$key])) { + // Disallow empty values except for the number zero. + // TODO: create a utility for this since it's needed in many places + if (!array_key_exists($key, $params) || (empty($params[$key]) && $params[$key] !== 0 && $params[$key] !== '0')) { $unmatched[] = $key; } } @@ -251,7 +253,13 @@ function civicrm_api3_create_success($values = 1, $params = array( else { $result['values'] = $values; } - + if(!empty($params['options']['metadata'])) { + // we've made metadata an array but only supporting 'fields' atm + if(in_array('fields', $params['options']['metadata'])) { + $fields = civicrm_api3($entity, 'getfields', array('action' => $action)); + $result['metadata']['fields'] = $fields['values']; + } + } return array_merge($result, $extraReturnValues); } @@ -382,6 +390,8 @@ function _civicrm_api3_store_values(&$fields, &$params, &$values) { * others that use the query object. Note that this function passes permission information in. * The others don't * + * * Ideally this would be merged with _civicrm_get_query_object but we need to resolve differences in what the + * 2 variants call * @param $entity * @param array $params as passed into api get or getcount function * @param array $additional_options @@ -453,7 +463,6 @@ function _civicrm_api3_get_using_query_object($entity, $params, $additional_opti } $skipPermissions = CRM_Utils_Array::value('check_permissions', $params)? 0 :1; - list($entities, $options) = CRM_Contact_BAO_Query::apiQuery( $newParams, $returnProperties, @@ -472,6 +481,45 @@ function _civicrm_api3_get_using_query_object($entity, $params, $additional_opti return $entities; } +/** + * get dao query object based on input params + * Ideally this would be merged with _civicrm_get_using_query_object but we need to resolve differences in what the + * 2 variants call + * + * @param array $params + * @param string $mode + * @param string $entity + * @return CRM_Core_DAO query object + */ +function _civicrm_api3_get_query_object($params, $mode, $entity) { + $options = _civicrm_api3_get_options_from_params($params, TRUE, $entity, 'get'); + $sort = CRM_Utils_Array::value('sort', $options, NULL); + $offset = CRM_Utils_Array::value('offset', $options); + $rowCount = CRM_Utils_Array::value('limit', $options); + $inputParams = CRM_Utils_Array::value('input_params', $options, array()); + $returnProperties = CRM_Utils_Array::value('return', $options, NULL); + if (empty($returnProperties)) { + $returnProperties = CRM_Contribute_BAO_Query::defaultReturnProperties($mode); + } + + $newParams = CRM_Contact_BAO_Query::convertFormValues($inputParams); + $query = new CRM_Contact_BAO_Query($newParams, $returnProperties, NULL, + FALSE, FALSE, $mode + ); + list($select, $from, $where, $having) = $query->query(); + + $sql = "$select $from $where $having"; + + if (!empty($sort)) { + $sql .= " ORDER BY $sort "; + } + if(!empty($rowCount)) { + $sql .= " LIMIT $offset, $rowCount "; + } + $dao = CRM_Core_DAO::executeQuery($sql); + return array($dao, $query); +} + /** * Function transfers the filters being passed into the DAO onto the params object */ @@ -557,6 +605,7 @@ function _civicrm_api3_dao_set_filter(&$dao, $params, $unique = TRUE, $entity) { $dao->selectAdd($allfields[$uniqueVal]); } } + $dao->setApiFilter($params); } /** @@ -697,7 +746,9 @@ function _civicrm_api3_apply_options_to_dao(&$params, &$dao, $entity) { $options = _civicrm_api3_get_options_from_params($params,FALSE,$entity); if(!$options['is_count']) { - $dao->limit((int)$options['offset'], (int)$options['limit']); + if(!empty($options['limit'])) { + $dao->limit((int)$options['offset'], (int)$options['limit']); + } if (!empty($options['sort'])) { $dao->orderBy($options['sort']); } @@ -752,12 +803,15 @@ function _civicrm_api3_get_unique_name_array(&$bao) { * @static void * @access public */ -function _civicrm_api3_dao_to_array($dao, $params = NULL, $uniqueFields = TRUE, $entity = "") { +function _civicrm_api3_dao_to_array($dao, $params = NULL, $uniqueFields = TRUE, $entity = "", $autoFind = TRUE) { $result = array(); if(isset($params['options']) && CRM_Utils_Array::value('is_count', $params['options'])) { return $dao->count(); } - if (empty($dao) || !$dao->find()) { + if (empty($dao)) { + return array(); + } + if ($autoFind && !$dao->find()) { return array(); } @@ -833,9 +887,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 ); @@ -843,6 +913,101 @@ function _civicrm_api3_custom_format_params($params, &$values, $extends, $entity } } +/** + * 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 @@ -963,9 +1128,9 @@ function _civicrm_api3_api_check_permission($entity, $action, &$params, $throw = */ function _civicrm_api3_basic_get($bao_name, &$params, $returnAsSuccess = TRUE, $entity = "") { $bao = new $bao_name(); - _civicrm_api3_dao_set_filter($bao, $params, TRUE,$entity); + _civicrm_api3_dao_set_filter($bao, $params, TRUE, $entity); if ($returnAsSuccess) { - return civicrm_api3_create_success(_civicrm_api3_dao_to_array($bao, $params, FALSE, $entity), $params, $entity); + 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); @@ -1005,6 +1170,16 @@ 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]); @@ -1197,7 +1372,8 @@ function _civicrm_api3_validate_date(&$params, &$fieldName, &$fieldInfo) { 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']]); + $format = ($fieldInfo['type'] == CRM_Utils_Type::T_DATE) ? 'Ymd000000' : 'YmdHis'; + $params[$fieldInfo['name']] = CRM_Utils_Date::processDate($params[$fieldInfo['name']], NULL, FALSE, $format); } if ((CRM_Utils_Array::value('name', $fieldInfo) != $fieldName) && CRM_Utils_Array::value($fieldName, $params)) { //If the unique field name differs from the db name & is set handle it here @@ -1571,12 +1747,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]); @@ -1644,8 +1817,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) ); }