/**
 * Set defaults for api.getlist
 *
 * @param $entity string
 * @param $request array
 */
function _civicrm_api3_generic_getList_defaults($entity, &$request)
{
    $config = CRM_Core_Config::singleton();
    $fields = _civicrm_api_get_fields($entity);
    $defaults = array('page_num' => 1, 'input' => '', 'image_field' => NULL, 'id_field' => 'id', 'params' => array());
    // Find main field from meta
    foreach (array('sort_name', 'title', 'label', 'name') as $field) {
        if (isset($fields[$field])) {
            $defaults['label_field'] = $defaults['search_field'] = $field;
            break;
        }
    }
    foreach (array('description') as $field) {
        if (isset($fields[$field])) {
            $defaults['description_field'] = $field;
            break;
        }
    }
    $resultsPerPage = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'search_autocomplete_count', NULL, 10);
    $request += $defaults;
    // Default api params
    $params = array('options' => array('limit' => $resultsPerPage + 1, 'offset' => ($request['page_num'] - 1) * $resultsPerPage, 'sort' => $request['label_field']), 'sequential' => 1);
    // When searching e.g. autocomplete
    if ($request['input']) {
        $params[$request['search_field']] = array('LIKE' => ($config->includeWildCardInName ? '%' : '') . $request['input'] . '%');
    }
    // When looking up a field e.g. displaying existing record
    if (!empty($request['id'])) {
        if (is_string($request['id']) && strpos(',', $request['id'])) {
            $request['id'] = explode(',', $request['id']);
        }
        $params[$request['id_field']] = is_array($request['id']) ? array('IN' => $request['id']) : $request['id'];
    }
    $request['params'] += $params;
}
Exemple #2
0
/**
 * Get information about fields for a given api request.
 *
 * Getfields information is used for documentation, validation, default setting
 * We first query the scheme using the $dao->fields function & then augment
 * that information by calling the _spec functions that apply to the relevant function
 * Note that we use 'unique' field names as described in the xml/schema files
 * for get requests & just field name for create. This is because some get functions
 * access multiple objects e.g. contact api accesses is_deleted from the activity
 * table & from the contact table
 *
 * @param array $apiRequest
 *   Api request as an array. Keys are.
 *   - entity: string
 *   - action: string
 *   - version: string
 *   - 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, $unique = TRUE)
{
    static $results = array();
    if (CRM_Utils_Array::value('cache_clear', $apiRequest['params'])) {
        $results = array();
        // we will also clear pseudoconstants here - should potentially be moved to relevant BAO classes
        CRM_Core_PseudoConstant::flush();
        if (!empty($apiRequest['params']['fieldname'])) {
            CRM_Utils_PseudoConstant::flushConstant($apiRequest['params']['fieldname']);
        }
        if (!empty($apiRequest['params']['option_group_id'])) {
            $optionGroupName = civicrm_api('option_group', 'getvalue', array('version' => 3, 'id' => $apiRequest['params']['option_group_id'], 'return' => 'name'));
            if (is_string($optionGroupName)) {
                CRM_Utils_PseudoConstant::flushConstant(_civicrm_api_get_camel_name($optionGroupName));
            }
        }
    }
    $entity = $apiRequest['entity'];
    $lowercase_entity = _civicrm_api_get_entity_name_from_camel($entity);
    $subentity = CRM_Utils_Array::value('contact_type', $apiRequest['params']);
    $action = CRM_Utils_Array::value('action', $apiRequest['params']);
    $sequential = empty($apiRequest['params']['sequential']) ? 0 : 1;
    $apiRequest['params']['options'] = CRM_Utils_Array::value('options', $apiRequest['params'], array());
    $optionsToResolve = (array) CRM_Utils_Array::value('get_options', $apiRequest['params']['options'], array());
    if (!$action || $action == 'getvalue' || $action == 'getcount') {
        $action = 'get';
    }
    // 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])) {
        return $results[$entity . $subentity][$action][$sequential];
    }
    // defaults based on data model and API policy
    switch ($action) {
        case 'getfields':
            $values = _civicrm_api_get_fields($entity, FALSE, $apiRequest['params']);
            return civicrm_api3_create_success($values, $apiRequest['params'], $entity, 'getfields');
        case 'create':
        case 'update':
        case 'replace':
            $unique = FALSE;
        case 'get':
        case 'getsingle':
        case 'getcount':
        case 'getstat':
            $metadata = _civicrm_api_get_fields($apiRequest['entity'], $unique, $apiRequest['params']);
            if (empty($metadata['id'])) {
                // if id is not set we will set it eg. 'id' from 'case_id', case_id will be an alias
                if (!empty($metadata[strtolower($apiRequest['entity']) . '_id'])) {
                    $metadata['id'] = $metadata[$lowercase_entity . '_id'];
                    unset($metadata[$lowercase_entity . '_id']);
                    $metadata['id']['api.aliases'] = array($lowercase_entity . '_id');
                }
            } else {
                // really the preference would be to set the unique name in the xml
                // question is which is a less risky fix this close to a release - setting in xml for the known failure
                // (note) or setting for all api where fields is returning 'id' & we want to accept 'note_id' @ the api layer
                // nb we don't officially accept note_id anyway - rationale here is more about centralising a now-tested
                // inconsistency
                $metadata['id']['api.aliases'] = array($lowercase_entity . '_id');
            }
            break;
        case 'delete':
            $metadata = array('id' => array('title' => $entity . ' ID', 'api.required' => 1, 'api.aliases' => array($lowercase_entity . '_id'), 'type' => CRM_Utils_Type::T_INT));
            break;
            // Note: adding setvalue case here instead of in a generic spec function because
            // some APIs override the generic setvalue fn which causes the generic spec to be overlooked.
        // Note: adding setvalue case here instead of in a generic spec function because
        // some APIs override the generic setvalue fn which causes the generic spec to be overlooked.
        case 'setvalue':
            $metadata = array('field' => array('title' => 'Field name', 'api.required' => 1, 'type' => CRM_Utils_Type::T_STRING), 'id' => array('title' => $entity . ' ID', 'api.required' => 1, 'type' => CRM_Utils_Type::T_INT), 'value' => array('title' => 'Value', 'description' => "Field value to set", 'api.required' => 1));
            if (array_intersect(array('all', 'field'), $optionsToResolve)) {
                $options = civicrm_api3_generic_getfields(array('entity' => $entity, array('params' => array('action' => 'create'))));
                $metadata['field']['options'] = CRM_Utils_Array::collect('title', $options['values']);
            }
            break;
        default:
            // oddballs are on their own
            $metadata = array();
    }
    // Normalize this for the sake of spec funcions
    $apiRequest['params']['options']['get_options'] = $optionsToResolve;
    // find any supplemental information
    $hypApiRequest = array('entity' => $apiRequest['entity'], 'action' => $action, 'version' => $apiRequest['version']);
    try {
        list($apiProvider, $hypApiRequest) = \Civi::service('civi_api_kernel')->resolve($hypApiRequest);
        if (isset($hypApiRequest['function'])) {
            $helper = '_' . $hypApiRequest['function'] . '_spec';
        } else {
            // not implemented MagicFunctionProvider
            $helper = NULL;
        }
    } catch (\Civi\API\Exception\NotImplementedException $e) {
        $helper = NULL;
    }
    if (function_exists($helper)) {
        // alter
        $helper($metadata, $apiRequest);
    }
    foreach ($metadata as $fieldname => $fieldSpec) {
        // Ensure 'name' is set
        if (!isset($fieldSpec['name'])) {
            $metadata[$fieldname]['name'] = $fieldname;
        }
        _civicrm_api3_generic_get_metadata_options($metadata, $apiRequest, $fieldname, $fieldSpec);
        // Convert options to "sequential" format
        if ($sequential && !empty($metadata[$fieldname]['options'])) {
            $metadata[$fieldname]['options'] = CRM_Utils_Array::makeNonAssociative($metadata[$fieldname]['options']);
        }
    }
    $results[$entity][$action][$sequential] = civicrm_api3_create_success($metadata, $apiRequest['params'], $entity, 'getfields');
    return $results[$entity][$action][$sequential];
}
 /**
  * Joins onto an fk field
  *
  * Adds one or more joins to the query to make this field available for use in a clause.
  *
  * Enforces permissions at the api level and by appending the acl clause for that entity to the join.
  *
  * @param $fkFieldName
  * @return array|null
  *   Returns the table and field name for adding this field to a SELECT or WHERE clause
  * @throws \API_Exception
  * @throws \Civi\API\Exception\UnauthorizedException
  */
 private function addFkField($fkFieldName)
 {
     $stack = explode('.', $fkFieldName);
     if (count($stack) < 2) {
         return NULL;
     }
     $prev = 'a';
     foreach ($stack as $depth => $fieldName) {
         // Setup variables then skip the first level
         if (!$depth) {
             $fk = $fieldName;
             // We only join on core fields
             // @TODO: Custom contact ref fields could be supported too
             if (!in_array($fk, $this->entityFieldNames)) {
                 return NULL;
             }
             $fkField =& $this->apiFieldSpec[$fk];
             continue;
         }
         // More than 4 joins deep seems excessive - DOS attack?
         if ($depth > self::MAX_JOINS) {
             throw new UnauthorizedException("Maximum number of joins exceeded for api.{$this->entity}.get in parameter {$fkFieldName}");
         }
         if (!isset($fkField['FKApiName']) && !isset($fkField['FKClassName'])) {
             // Join doesn't exist - might be another param with a dot in it for some reason, we'll just ignore it.
             return NULL;
         }
         // Ensure we have permission to access the other api
         if (!$this->checkPermissionToJoin($fkField['FKApiName'], array_slice($stack, 0, $depth))) {
             throw new UnauthorizedException("Authorization failed to join onto {$fkField['FKApiName']} api in parameter {$fkFieldName}");
         }
         if (!isset($fkField['FKApiSpec'])) {
             $fkField['FKApiSpec'] = \_civicrm_api_get_fields($fkField['FKApiName']);
         }
         $fieldInfo = \CRM_Utils_Array::value($fieldName, $fkField['FKApiSpec']);
         // FIXME: What if the foreign key is not the "id" column?
         if (!$fieldInfo || !isset($fkField['FKApiSpec']['id'])) {
             // Join doesn't exist - might be another param with a dot in it for some reason, we'll just ignore it.
             return NULL;
         }
         $fkTable = \CRM_Core_DAO_AllCoreTables::getTableForClass($fkField['FKClassName']);
         $tableAlias = implode('_to_', array_slice($stack, 0, $depth)) . "_to_{$fkTable}";
         $joinClause = "LEFT JOIN {$fkTable} {$tableAlias} ON {$prev}.{$fk} = {$tableAlias}.id";
         // Add acl condition
         $joinCondition = $this->getAclClause($tableAlias, $fkField['FKClassName']);
         if ($joinCondition !== NULL) {
             $joinClause .= " AND {$joinCondition}";
         }
         $this->query->join($tableAlias, $joinClause);
         if (strpos($fieldName, 'custom_') === 0) {
             list($tableAlias, $fieldName) = $this->addCustomField($fieldInfo, $tableAlias);
         }
         // Get ready to recurse to the next level
         $fk = $fieldName;
         $fkField =& $fkField['FKApiSpec'][$fieldName];
         $prev = $tableAlias;
     }
     return array($tableAlias, $fieldName);
 }
Exemple #4
0
/**
 * Get information about fields for a given api request. Getfields information
 * is used for documentation, validation, default setting
 * We first query the scheme using the $dao->fields function & then augment
 * that information by calling the _spec functions that apply to the relevant function
 * Note that we use 'unique' field names as described in the xml/schema files
 * for get requests & just field name for create. This is because some get functions
 * access multiple objects e.g. contact api accesses is_deleted from the activity
 * table & from the contact table
 *
 * @param array $apiRequest api request as an array. Keys are
 *  - entity: string
 *  - action: string
 *  - version: string
 *  - function: callback (mixed)
 *  - params: array, varies
 *  @return array API success object
 */
function civicrm_api3_generic_getfields($apiRequest)
{
    static $results = array();
    if (CRM_Utils_Array::value('cache_clear', $apiRequest['params'])) {
        $results = array();
        // we will also clear pseudoconstants here - should potentially be moved to relevant BAO classes
        CRM_Core_PseudoConstant::flush();
        if (!empty($apiRequest['params']['fieldname'])) {
            CRM_Utils_PseudoConstant::flushConstant($apiRequest['params']['fieldname']);
        }
        if (!empty($apiRequest['params']['option_group_id'])) {
            $optionGroupName = civicrm_api('option_group', 'getvalue', array('version' => 3, 'id' => $apiRequest['params']['option_group_id'], 'return' => 'name'));
            if (is_string($optionGroupName)) {
                CRM_Utils_PseudoConstant::flushConstant(_civicrm_api_get_camel_name($optionGroupName));
            }
        }
    }
    $entity = _civicrm_api_get_camel_name($apiRequest['entity']);
    $lcase_entity = _civicrm_api_get_entity_name_from_camel($entity);
    $subentity = CRM_Utils_Array::value('contact_type', $apiRequest['params']);
    $action = strtolower(CRM_Utils_Array::value('action', $apiRequest['params']));
    $sequential = empty($apiRequest['params']) ? 0 : 1;
    $apiOptions = CRM_Utils_Array::value('options', $apiRequest['params'], array());
    if ($action == 'getvalue' || $action == 'getvalue' || $action == 'getcount') {
        $action = 'get';
    }
    if (empty($action)) {
        $action = 'get';
    }
    // determines whether to use unique field names - seem comment block above
    $unique = TRUE;
    if (empty($apiOptions) && isset($results[$entity . $subentity]) && isset($action, $results[$entity . $subentity]) && isset($action, $results[$entity . $subentity][$sequential])) {
        return $results[$entity . $subentity][$action][$sequential];
    }
    // defaults based on data model and API policy
    switch ($action) {
        case 'getfields':
            $values = _civicrm_api_get_fields($entity, false, $apiRequest['params']);
            return civicrm_api3_create_success($values, $apiRequest['params'], $entity, 'getfields');
        case 'create':
        case 'update':
        case 'replace':
            $unique = FALSE;
        case 'get':
            $metadata = _civicrm_api_get_fields($apiRequest['entity'], $unique, $apiRequest['params']);
            if (empty($metadata['id'])) {
                // if id is not set we will set it eg. 'id' from 'case_id', case_id will be an alias
                if (!empty($metadata[strtolower($apiRequest['entity']) . '_id'])) {
                    $metadata['id'] = $metadata[$lcase_entity . '_id'];
                    unset($metadata[$lcase_entity . '_id']);
                    $metadata['id']['api.aliases'] = array($lcase_entity . '_id');
                }
            } else {
                // really the preference would be to set the unique name in the xml
                // question is which is a less risky fix this close to a release - setting in xml for the known failure
                // (note) or setting for all api where fields is returning 'id' & we want to accept 'note_id' @ the api layer
                // nb we don't officially accept note_id anyway - rationale here is more about centralising a now-tested
                // inconsistency
                $metadata['id']['api.aliases'] = array($lcase_entity . '_id');
            }
            break;
        case 'delete':
            $metadata = array('id' => array('title' => 'Unique Identifier', 'api.required' => 1, 'api.aliases' => array($lcase_entity . '_id'), 'type' => CRM_Utils_Type::T_INT));
            break;
        case 'getoptions':
            $metadata = array('field' => array('title' => 'Field to retrieve options for', 'api.required' => 1), 'context' => array('title' => 'Context string'));
            break;
        default:
            // oddballs are on their own
            $metadata = array();
    }
    // find any supplemental information
    $hypApiRequest = array('entity' => $apiRequest['entity'], 'action' => $action, 'version' => $apiRequest['version']);
    $hypApiRequest += _civicrm_api_resolve($hypApiRequest);
    $helper = '_' . $hypApiRequest['function'] . '_spec';
    if (function_exists($helper)) {
        // alter
        $helper($metadata, $apiRequest);
    }
    $fieldsToResolve = (array) CRM_Utils_Array::value('get_options', $apiOptions, array());
    foreach ($metadata as $fieldname => $fieldSpec) {
        _civicrm_api3_generic_get_metadata_options($metadata, $apiRequest['entity'], $fieldname, $fieldSpec, $fieldsToResolve);
    }
    $results[$entity][$action][$sequential] = civicrm_api3_create_success($metadata, $apiRequest['params'], NULL, 'getfields');
    return $results[$entity][$action][$sequential];
}
/**
 * Get information about fields for a given api request. Getfields information
 * is used for documentation, validation, default setting
 * We first query the scheme using the $dao->fields function & then augment
 * that information by calling the _spec functions that apply to the relevant function
 * Note that we use 'unique' field names as described in the xml/schema files
 * for get requests & just field name for create. This is because some get functions
 * access multiple objects e.g. contact api accesses is_deleted from the activity
 * table & from the contact table
 *
 * @param array $apiRequest api request as an array. Keys are
 *  - entity: string
 *  - action: string
 *  - version: string
 *  - function: callback (mixed)
 *  - params: array, varies
 *  @return array API success object
 */
function civicrm_api3_generic_getfields($apiRequest)
{
    static $results = array();
    if (CRM_Utils_Array::value('cache_clear', $apiRequest['params'])) {
        $results = array();
    }
    $entity = _civicrm_api_get_camel_name($apiRequest['entity']);
    $lcase_entity = _civicrm_api_get_entity_name_from_camel($entity);
    $subentity = CRM_Utils_Array::value('contact_type', $apiRequest['params']);
    $action = strtolower(CRM_Utils_Array::value('action', $apiRequest['params']));
    if ($action == 'getvalue' || $action == 'getvalue' || $action == 'getcount') {
        $action = 'get';
    }
    if (empty($action)) {
        if (CRM_Utils_Array::value($entity . $subentity, $results) && CRM_Utils_Array::value('values', $results[$entity . $subentity])) {
            return $results[$entity . $subentity];
        } else {
            $values = _civicrm_api_get_fields($entity);
            $results[$entity] = civicrm_api3_create_success($values, $apiRequest['params'], $entity, 'getfields');
            return $results[$entity];
        }
    }
    // determines whether to use unique field names - seem comment block above
    $unique = TRUE;
    if (isset($results[$entity . $subentity]) && CRM_Utils_Array::value($action, $results[$entity])) {
        return $results[$entity . $subentity][$action];
    }
    // defaults based on data model and API policy
    switch ($action) {
        case 'getfields':
            $values = _civicrm_api_get_fields($entity, $apiRequest['params']);
            $results[$entity][$action] = civicrm_api3_create_success($values, $apiRequest['params'], $entity, 'getfields');
            return $results[$entity][$action];
        case 'create':
        case 'update':
        case 'replace':
            $unique = FALSE;
        case 'get':
            $metadata = _civicrm_api_get_fields($apiRequest['entity'], $unique, $apiRequest['params']);
            if (empty($metadata['id']) && !empty($metadata[$apiRequest['entity'] . '_id'])) {
                $metadata['id'] = $metadata[$lcase_entity . '_id'];
                $metadata['id']['api.aliases'] = array($lcase_entity . '_id');
                unset($metadata[$lcase_entity . '_id']);
            }
            break;
        case 'delete':
            $metadata = array('id' => array('title' => 'Unique Identifier', 'api.required' => 1, 'api.aliases' => array($lcase_entity . '_id')));
            break;
        default:
            // oddballs are on their own
            $metadata = array();
    }
    // find any supplemental information
    $hypApiRequest = array('entity' => $apiRequest['entity'], 'action' => $action, 'version' => $apiRequest['version']);
    $hypApiRequest += _civicrm_api_resolve($hypApiRequest);
    $helper = '_' . $hypApiRequest['function'] . '_spec';
    if (function_exists($helper)) {
        // alter
        $helper($metadata);
    }
    foreach ($metadata as $fieldname => $field) {
        if (array_key_exists('pseudoconstant', $field) && !CRM_Utils_Array::value('FKClassName', $field)) {
            $options = civicrm_api('constant', 'get', array('version' => 3, 'name' => $field['pseudoconstant']));
            if (is_array(CRM_Utils_Array::value('values', $options))) {
                $metadata[$fieldname]['options'] = $options['values'];
            }
        }
        if (array_key_exists('enumValues', $field)) {
            $metadata[$fieldname]['options'] = explode(',', $field['enumValues']);
        }
    }
    $results[$entity][$action] = civicrm_api3_create_success($metadata, $apiRequest['params'], NULL, 'getfields');
    return $results[$entity][$action];
}