Merge pull request #6450 from civicrm/4.6
[civicrm-core.git] / api / v3 / Generic.php
index cef725e0850aea36088db56d168fa94c52dc2348..4994e1e9ec1e96a7c9aba2f290860572a6a1d95a 100644 (file)
@@ -3,7 +3,7 @@
  +--------------------------------------------------------------------+
  | CiviCRM version 4.6                                                |
  +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC (c) 2004-2014                                |
+ | Copyright CiviCRM LLC (c) 2004-2015                                |
  +--------------------------------------------------------------------+
  | This file is a part of CiviCRM.                                    |
  |                                                                    |
  *   - 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();
@@ -75,14 +78,15 @@ function civicrm_api3_generic_getfields($apiRequest) {
   $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']) ? 0 : 1;
-  $apiOptions = CRM_Utils_Array::value('options', $apiRequest['params'], array());
+  $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';
   }
-  // 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])
+  // 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];
   }
@@ -123,25 +127,36 @@ function civicrm_api3_generic_getfields($apiRequest) {
       $metadata = array(
         'id' => array(
           'title' => $entity . ' ID',
-          'name' => 'id',
           'api.required' => 1,
           'api.aliases' => array($lowercase_entity . '_id'),
           'type' => CRM_Utils_Type::T_INT,
         ));
       break;
 
-    case 'getoptions':
+    // 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(
-          'name' => 'field',
           'title' => 'Field name',
           'api.required' => 1,
+          'type' => CRM_Utils_Type::T_STRING,
         ),
-        'context' => array(
-          'name' => 'context',
-          'title' => 'Context',
+        '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:
@@ -149,6 +164,9 @@ function civicrm_api3_generic_getfields($apiRequest) {
       $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 {
@@ -169,20 +187,86 @@ function civicrm_api3_generic_getfields($apiRequest) {
     $helper($metadata, $apiRequest);
   }
 
-  $fieldsToResolve = (array) CRM_Utils_Array::value('get_options', $apiOptions, array());
-
   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, $fieldsToResolve);
+    _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];
 }
 
+/**
+ * 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');
+}
+
+
+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.
  *
@@ -267,9 +351,10 @@ function civicrm_api3_generic_getvalue($apiRequest) {
  *
  * @param array $params
  */
-function _civicrm_api3_generic_getrefcount_spec(&$params) {
+function _civicrm_api3_generic_getrefcount_spec(&$params, $apiRequest) {
   $params['id']['api.required'] = 1;
-  $params['id']['title'] = 'Entity ID';
+  $params['id']['title'] = $apiRequest['entity'] . ' ID';
+  $params['id']['type'] = CRM_Utils_Type::T_INT;
 }
 
 /**
@@ -345,6 +430,38 @@ function civicrm_api3_generic_getoptions($apiRequest) {
   return civicrm_api3_create_success($options, $apiRequest['params'], $apiRequest['entity'], 'getoptions');
 }
 
+/**
+ * Provide metadata for this generic action
+ *
+ * @param $params
+ * @param $apiRequest
+ */
+function _civicrm_api3_generic_getoptions_spec(&$params, $apiRequest) {
+  $params += array(
+    'field' => array(
+      'title' => 'Field name',
+      'api.required' => 1,
+      'type' => CRM_Utils_Type::T_STRING,
+    ),
+    'context' => array(
+      'title' => 'Context',
+      'type' => CRM_Utils_Type::T_STRING,
+      'options' => CRM_Core_DAO::buildOptionsContext(),
+    ),
+  );
+
+  // Add available fields if requested
+  if (array_intersect(array('all', 'field'), $apiRequest['params']['options']['get_options'])) {
+    $fields = civicrm_api3_generic_getfields(array('entity' => $apiRequest['entity'], array('params' => array('action' => 'create'))));
+    $params['field']['options'] = array();
+    foreach ($fields['values'] as $name => $field) {
+      if (isset($field['pseudoconstant']) || CRM_Utils_Array::value('type', $field) == CRM_Utils_Type::T_BOOLEAN) {
+        $params['field']['options'][$name] = CRM_Utils_Array::value('title', $field, $name);
+      }
+    }
+  }
+}
+
 /**
  * Get metadata.
  *
@@ -365,19 +482,30 @@ function civicrm_api3_generic_getoptions($apiRequest) {
  *   Field currently being processed.
  * @param array $fieldSpec
  *   Metadata for that field.
- * @param array $fieldsToResolve
- *   Anny field resolutions specifically requested.
  */
-function _civicrm_api3_generic_get_metadata_options(&$metadata, $apiRequest, $fieldname, $fieldSpec, $fieldsToResolve) {
+function _civicrm_api3_generic_get_metadata_options(&$metadata, $apiRequest, $fieldname, $fieldSpec) {
   if (empty($fieldSpec['pseudoconstant']) && empty($fieldSpec['option_group_id'])) {
     return;
   }
 
+  $fieldsToResolve = $apiRequest['params']['options']['get_options'];
+
   if (!empty($metadata[$fieldname]['options']) || (!in_array($fieldname, $fieldsToResolve) && !in_array('all', $fieldsToResolve))) {
     return;
   }
 
-  $options = civicrm_api($apiRequest['entity'], 'getoptions', array('version' => 3, 'field' => $fieldname, 'sequential' => !empty($apiRequest['params']['sequential'])));
+  // Allow caller to specify context
+  $context = CRM_Utils_Array::value('get_options_context', $apiRequest['params']['options']);
+  // Default to api action if it is a supported context.
+  if (!$context) {
+    $action = CRM_Utils_Array::value('action', $apiRequest['params']);
+    $contexts = CRM_Core_DAO::buildOptionsContext();
+    if (isset($contexts[$action])) {
+      $context = $action;
+    }
+  }
+
+  $options = civicrm_api($apiRequest['entity'], 'getoptions', array('version' => 3, 'field' => $fieldname, 'context' => $context));
   if (is_array(CRM_Utils_Array::value('values', $options))) {
     $metadata[$fieldname]['options'] = $options['values'];
   }