From e8f50084dab14f4209d25445a4a288ab0cf9074e Mon Sep 17 00:00:00 2001 From: eileen Date: Wed, 11 May 2016 08:40:41 +1200 Subject: [PATCH] API code sync 4.7 to 4.6 --- api/v3/Generic.php | 78 ++++++++++++++++++- api/v3/utils.php | 69 +++++++++++----- .../phpunit/api/v3/SyntaxConformanceTest.php | 15 ++++ 3 files changed, 140 insertions(+), 22 deletions(-) diff --git a/api/v3/Generic.php b/api/v3/Generic.php index 666ccc3643..c5132cadc9 100644 --- a/api/v3/Generic.php +++ b/api/v3/Generic.php @@ -48,10 +48,13 @@ * - function: callback (mixed) * - params: array, varies * + * @param bool $unique + * Determines whether to key by unique field names (only affects get-type) actions + * * @return array * API success object */ -function civicrm_api3_generic_getfields($apiRequest) { +function civicrm_api3_generic_getfields($apiRequest, $unique = TRUE) { static $results = array(); if ((CRM_Utils_Array::value('cache_clear', $apiRequest['params']))) { $results = array(); @@ -82,8 +85,6 @@ function civicrm_api3_generic_getfields($apiRequest) { if (!$action || $action == 'getvalue' || $action == 'getcount') { $action = 'get'; } - // determines whether to use unique field names - see comment block above - $unique = TRUE; // If no options, return results from cache if (!$apiRequest['params']['options'] && isset($results[$entity . $subentity]) && isset($action, $results[$entity . $subentity]) && isset($action, $results[$entity . $subentity][$sequential])) { @@ -203,6 +204,77 @@ function civicrm_api3_generic_getfields($apiRequest) { return $results[$entity][$action][$sequential]; } +/** + * Get metadata for a field + * + * @param array $apiRequest + * + * @return array + * API success object + */ +function civicrm_api3_generic_getfield($apiRequest) { + $params = $apiRequest['params']; + $sequential = !empty($params['sequential']); + $fieldName = _civicrm_api3_api_resolve_alias($apiRequest['entity'], $params['name'], $params['action']); + if (!$fieldName) { + return civicrm_api3_create_error("The field '{$params['name']}' doesn't exist."); + } + // Turn off sequential to make the field easier to find + $apiRequest['params']['sequential'] = 0; + if (isset($params['get_options'])) { + $apiRequest['params']['options']['get_options_context'] = $params['get_options']; + $apiRequest['params']['options']['get_options'] = $fieldName; + } + $result = civicrm_api3_generic_getfields($apiRequest, FALSE); + $result = $result['values'][$fieldName]; + // Fix sequential options since we forced it off + if ($sequential && !empty($result['options'])) { + $result['options'] = CRM_Utils_Array::makeNonAssociative($result['options']); + } + return civicrm_api3_create_success($result, $apiRequest['params'], $apiRequest['entity'], 'getfield'); +} + +/** + * Get metadata for getfield action. + * + * @param array $params + * @param array $apiRequest + * + * @throws \CiviCRM_API3_Exception + * @throws \Exception + */ +function _civicrm_api3_generic_getfield_spec(&$params, $apiRequest) { + $params = array( + 'name' => array( + 'title' => 'Field name', + 'description' => 'Name or alias of field to lookup', + 'api.required' => 1, + 'type' => CRM_Utils_Type::T_STRING, + ), + 'action' => array( + 'title' => 'API Action', + 'api.required' => 1, + 'type' => CRM_Utils_Type::T_STRING, + 'api.aliases' => array('api_action'), + ), + 'get_options' => array( + 'title' => 'Get Options', + 'description' => 'Context for which to get field options, or null to skip fetching options.', + 'type' => CRM_Utils_Type::T_STRING, + 'options' => CRM_Core_DAO::buildOptionsContext(), + 'api.aliases' => array('context'), + ), + ); + // Add available options to these params if requested + if (array_intersect(array('all', 'action'), $apiRequest['params']['options']['get_options'])) { + $actions = civicrm_api3($apiRequest['entity'], 'getactions'); + $actions = array_combine($actions['values'], $actions['values']); + // Let's not go meta-crazy + CRM_Utils_Array::remove($actions, 'getactions', 'getoptions', 'getfields', 'getfield', 'getcount', 'getrefcount', 'getsingle', 'getlist', 'getvalue', 'setvalue', 'update'); + $params['action']['options'] = $actions; + } +} + /** * API return function to reformat results as count. * diff --git a/api/v3/utils.php b/api/v3/utils.php index f9e89304f1..3754a00017 100644 --- a/api/v3/utils.php +++ b/api/v3/utils.php @@ -373,7 +373,7 @@ function _civicrm_api3_get_DAO($name) { } /** - * Return the DAO of the function or Entity. + * Return the BAO name of the function or Entity. * * @param string $name * Is either a function of the api (civicrm_{entity}_create or the entity name. @@ -549,10 +549,16 @@ SELECT f.id, f.label, f.data_type, * Array of options (so we can modify the filter). * @param bool $getCount * Are we just after the count. + * @param int $mode + * This basically correlates to the component. + * @param null|array $defaultReturnProperties + * Default return properties for the entity + * (used if return not set - but don't do that - set return!). * * @return array + * @throws API_Exception */ -function _civicrm_api3_get_using_query_object($entity, $params, $additional_options = array(), $getCount = NULL) { +function _civicrm_api3_get_using_query_object($entity, $params, $additional_options = array(), $getCount = NULL, $mode = 1, $defaultReturnProperties = NULL) { $lowercase_entity = _civicrm_api_get_entity_name_from_camel($entity); // Convert id to e.g. contact_id if (empty($params[$lowercase_entity . '_id']) && isset($params['id'])) { @@ -571,7 +577,7 @@ function _civicrm_api3_get_using_query_object($entity, $params, $additional_opti CRM_Utils_Array::value('return', $additional_options, array()) ); if (empty($returnProperties)) { - $returnProperties = NULL; + $returnProperties = $defaultReturnProperties; } if (!empty($params['check_permissions'])) { // we will filter query object against getfields @@ -609,7 +615,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( + list($entities) = CRM_Contact_BAO_Query::apiQuery( $newParams, $returnProperties, NULL, @@ -618,7 +624,8 @@ function _civicrm_api3_get_using_query_object($entity, $params, $additional_opti $limit, $smartGroupCache, $getCount, - $skipPermissions + $skipPermissions, + $mode ); if ($getCount) { // only return the count of contacts @@ -674,14 +681,19 @@ function _civicrm_api3_get_query_object($params, $mode, $entity) { /** * Function transfers the filters being passed into the DAO onto the params object. * + * @deprecated DAO based retrieval is being phased out. + * * @param CRM_Core_DAO $dao * @param array $params * @param bool $unique + * @param array $extraSql + * API specific queries eg for event isCurrent would be converted to + * $extraSql['where'] = array('civicrm_event' => array('(start_date >= CURDATE() || end_date >= CURDATE())')); * * @throws API_Exception * @throws Exception */ -function _civicrm_api3_dao_set_filter(&$dao, $params, $unique = TRUE) { +function _civicrm_api3_dao_set_filter(&$dao, $params, $unique = TRUE, $extraSql = array()) { $entity = _civicrm_api_get_entity_name_from_dao($dao); $lowercase_entity = _civicrm_api_get_entity_name_from_camel($entity); if (!empty($params[$lowercase_entity . "_id"]) && empty($params['id'])) { @@ -737,6 +749,13 @@ function _civicrm_api3_dao_set_filter(&$dao, $params, $unique = TRUE) { } } } + if (!empty($extraSql['where'])) { + foreach ($extraSql['where'] as $table => $sqlWhere) { + foreach ($sqlWhere as $where) { + $dao->whereAdd($where); + } + } + } if (!empty($options['return']) && is_array($options['return']) && empty($options['is_count'])) { $dao->selectAdd(); // Ensure 'id' is included. @@ -876,7 +895,7 @@ function _civicrm_api3_get_options_from_params(&$params, $queryObject = FALSE, $ return $options; } //here comes the legacy support for $returnProperties, $inputParams e.g for contat_get - // if the queryobject is being used this should be used + // if the query object is being used this should be used $inputParams = array(); $legacyreturnProperties = array(); $otherVars = array( @@ -975,6 +994,8 @@ function _civicrm_api3_get_unique_name_array(&$bao) { /** * Converts an DAO object to an array. * + * @deprecated - DAO based retrieval is being phased out. + * * @param CRM_Core_DAO $dao * Object to convert. * @param array $params @@ -1000,8 +1021,7 @@ function _civicrm_api3_dao_to_array($dao, $params = NULL, $uniqueFields = TRUE, return $dao->count; } - $fields = array_keys(_civicrm_api3_build_fields_array($dao, $uniqueFields)); - + $fields = array_keys(_civicrm_api3_build_fields_array($dao, FALSE)); while ($dao->fetch()) { $tmp = array(); foreach ($fields as $key) { @@ -1046,6 +1066,7 @@ function _civicrm_api3_custom_fields_are_required($entity, $params) { return TRUE; } } + /** * Converts an object to an array. * @@ -1247,7 +1268,7 @@ function formatCheckBoxField(&$checkboxFieldValue, $customFieldLabel, $entity) { * @param string $daoName * @param bool $return * - * @daoName string DAO to check params agains + * @daoName string DAO to check params against * * @return bool * Should the missing fields be returned as an array (core error created as default) @@ -1307,18 +1328,22 @@ function _civicrm_api3_check_required_fields($params, $daoName, $return = FALSE) * @param bool $returnAsSuccess * Return in api success format. * @param string $entity + * @param CRM_Utils_SQL_Select|NULL $sql + * Extra SQL bits to add to the query. For filtering current events, this might be: + * CRM_Utils_SQL_Select::fragment()->where('(start_date >= CURDATE() || end_date >= CURDATE())'); + * @param bool $uniqueFields + * Should unique field names be returned (for backward compatibility) * * @return array */ -function _civicrm_api3_basic_get($bao_name, &$params, $returnAsSuccess = TRUE, $entity = "") { - $bao = new $bao_name(); - _civicrm_api3_dao_set_filter($bao, $params, TRUE); +function _civicrm_api3_basic_get($bao_name, $params, $returnAsSuccess = TRUE, $entity = "", $sql = NULL, $uniqueFields = FALSE) { + $query = new \Civi\API\SelectQuery($bao_name, $params, $uniqueFields); + $query->merge($sql); + $result = $query->run(); if ($returnAsSuccess) { - 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, 'get'); + return civicrm_api3_create_success($result, $params, $entity, 'get'); } + return $result; } /** @@ -1361,7 +1386,7 @@ function _civicrm_api3_basic_create($bao_name, &$params, $entity = NULL) { 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 + //some weird 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 @@ -2257,6 +2282,9 @@ function _civicrm_api3_api_match_pseudoconstant_value(&$value, $options, $fieldN * fieldName or FALSE if the field does not exist */ function _civicrm_api3_api_resolve_alias($entity, $fieldName, $action = 'create') { + if (!$fieldName) { + return FALSE; + } if (strpos($fieldName, 'custom_') === 0 && is_numeric($fieldName[7])) { return $fieldName; } @@ -2275,7 +2303,7 @@ function _civicrm_api3_api_resolve_alias($entity, $fieldName, $action = 'create' return $meta[$fieldName]['name']; } foreach ($meta as $info) { - if ($fieldName == CRM_Utils_Array::value('uniqueName', $info)) { + if ($fieldName == $info['name'] || $fieldName == CRM_Utils_Array::value('uniqueName', $info)) { return $info['name']; } if (array_search($fieldName, CRM_Utils_Array::value('api.aliases', $info, array())) !== FALSE) { @@ -2338,6 +2366,7 @@ function _civicrm_api3_field_value_check(&$params, $fieldName) { * _civicrm_api3_basic_get but does not use DAO/BAO. This is useful for * small/mid-size data loaded from external JSON or XML documents. * + * @param $entity * @param array $params * API parameters. * @param array $records @@ -2346,7 +2375,9 @@ function _civicrm_api3_field_value_check(&$params, $fieldName) { * The property which defines the ID of a record * @param array $fields * List of filterable fields. + * * @return array + * @throws \API_Exception */ function _civicrm_api3_basic_array_get($entity, $params, $records, $idCol, $fields) { $options = _civicrm_api3_get_options_from_params($params, TRUE, $entity, 'get'); diff --git a/tests/phpunit/api/v3/SyntaxConformanceTest.php b/tests/phpunit/api/v3/SyntaxConformanceTest.php index aa403ce360..e5d5b45164 100644 --- a/tests/phpunit/api/v3/SyntaxConformanceTest.php +++ b/tests/phpunit/api/v3/SyntaxConformanceTest.php @@ -665,6 +665,21 @@ class api_v3_SyntaxConformanceTest extends CiviUnitTestCase { } } + /** + * Test that an invalid sort is ignored & there is no breakage. + * + * @dataProvider entities_get + * @param $Entity + */ + public function testEmptyParam_getInvalidSort($Entity) { + + if (in_array($Entity, $this->toBeImplemented['get'])) { + // $this->markTestIncomplete("civicrm_api3_{$Entity}_get to be implemented"); + return; + } + $this->callAPISuccess($Entity, 'Get', array('options' => array('sort' => 'Silly thing'))); + } + /** * @dataProvider entities_get * @param $Entity -- 2.25.1