Update API option matching and validation CRM-12464
authorColeman Watts <coleman@civicrm.org>
Fri, 7 Jun 2013 18:05:54 +0000 (11:05 -0700)
committerColeman Watts <coleman@civicrm.org>
Fri, 7 Jun 2013 18:05:54 +0000 (11:05 -0700)
----------------------------------------
* CRM-12464: Add PseudoConstants to Schema Metadata
  http://issues.civicrm.org/jira/browse/CRM-12464

api/v3/Generic.php
api/v3/examples/Activity/ContactRefCustomField.php
api/v3/examples/Activity/ContactRefCustomFieldGet.php
api/v3/examples/Activity/DateTimeHigh.php
api/v3/examples/Activity/DateTimeLow.php
api/v3/examples/Activity/GetTargetandAssignee.php
api/v3/examples/Activity/ReturnAssigneeContact.php
api/v3/examples/ActivityGetfields.php
api/v3/utils.php
tests/phpunit/api/v3/ActivityTest.php

index e86bd43d63c53babfca87b3b1f6b6bbc7a5a0191..6a70b2899927a7807847e415e2b6f8e6c019d021 100644 (file)
@@ -111,7 +111,7 @@ function civicrm_api3_generic_getfields($apiRequest) {
   $fieldsToResolve = CRM_Utils_Array::value('get_options', $apiOptions, array());
 
   foreach ($metadata as $fieldname => $fieldSpec) {
-    _civicrm_api3_generic_get_metadata_options($metadata, $fieldname, $fieldSpec, $fieldsToResolve);
+    _civicrm_api3_generic_get_metadata_options($metadata, $apiRequest['entity'], $fieldname, $fieldSpec, $fieldsToResolve);
   }
 
   $results[$entity][$action] = civicrm_api3_create_success($metadata, $apiRequest['params'], NULL, 'getfields');
@@ -202,10 +202,16 @@ function civicrm_api3_generic_replace($apiRequest) {
  * @return array of results
  */
 function civicrm_api3_generic_getoptions($apiRequest) {
+  // Resolve aliases
+  $fieldName = _civicrm_api3_api_resolve_alias($apiRequest['entity'], $apiRequest['params']['field']);
+  if (!$fieldName) {
+    return civicrm_api3_create_error("The field '{$apiRequest['params']['field']}' doesn't exist.");
+  }
+
   $daoName = _civicrm_api3_get_DAO($apiRequest['entity']);
-  $options = $daoName::buildOptions($apiRequest['params']['field']);
+  $options = $daoName::buildOptions($fieldName);
   if ($options === FALSE) {
-    return civicrm_api3_create_error("The field '{$apiRequest['params']['field']}' either doesn't exist or has no associated option list.");
+    return civicrm_api3_create_error("The field '{$fieldName}' has no associated option list.");
   }
   return civicrm_api3_create_success($options);
 }
@@ -226,27 +232,20 @@ function civicrm_api3_generic_getoptions($apiRequest) {
  * @param array $fieldSpec metadata for that field
  * @param array $fieldsToResolve anny field resolutions specifically requested
  */
-function _civicrm_api3_generic_get_metadata_options(&$metadata, $fieldname, $fieldSpec, $fieldsToResolve){
-  if (array_key_exists('enumValues', $fieldSpec)) {
-    // use of a space after the comma is inconsistent in xml
-    $enumStr = str_replace(', ', ',', $fieldSpec['enumValues']);
-    $metadata[$fieldname]['options'] = explode(',', $enumStr);
+function _civicrm_api3_generic_get_metadata_options(&$metadata, $entity, $fieldname, $fieldSpec, $fieldsToResolve){
+  if(empty($fieldSpec['pseudoconstant'])) {
     return;
   }
 
-  if(empty($fieldSpec['pseudoconstant'])){
-    return ;
-  }
-  elseif(!empty($fieldSpec['FKClassName']) && !in_array($fieldname, $fieldsToResolve)){
-    return;
-  }
   if(substr($fieldname, -3) == '_id'){
     $metadata[$fieldname]['api.aliases'][] = substr($fieldname, 0, -3);
   }
 
-  $pseudoParams = $fieldSpec['pseudoconstant'];
-  $pseudoParams['version'] = 3;
-  $options = civicrm_api('constant', 'get', $pseudoParams);
+  if (!in_array($fieldname, $fieldsToResolve)) {
+    return;
+  }
+
+  $options = civicrm_api($entity, 'getoptions', array('version' => 3, 'field' => $fieldname));
   if (is_array(CRM_Utils_Array::value('values', $options))) {
     $metadata[$fieldname]['options'] = $options['values'];
   }
index bee027469236ff627f9c094c767c1302defa826b..91bedb73a0aef29a21800fede2e2419526fafc1e 100644 (file)
@@ -36,7 +36,6 @@ function activity_create_expectedresult(){
   'values' => array( 
       '1' => array( 
           'id' => '1',
-          'source_contact_id' => '17',
           'source_record_id' => '',
           'activity_type_id' => '44',
           'subject' => 'test activity type id',
index 6ea6c40a6d19304cd955154986d463ce66ea6b73..1fe99eebea9fb8d2bf285aec74fb0ab04adb6024 100644 (file)
@@ -36,7 +36,6 @@ function activity_create_expectedresult(){
   'values' => array( 
       '1' => array( 
           'id' => '1',
-          'source_contact_id' => '17',
           'activity_type_id' => '44',
           'subject' => 'test activity type id',
           'activity_date_time' => '2011-06-02 14:36:13',
@@ -49,6 +48,7 @@ function activity_create_expectedresult(){
           'is_auto' => 0,
           'is_current_revision' => '1',
           'is_deleted' => 0,
+          'source_contact_id' => '17',
           'custom_2_id' => '17',
           'custom_2_1_id' => '17',
           'custom_2' => 'Contact, Test',
index b6fc059fd83054bdf177f3fd4e257366bb4ae945..a280514f0bbf6a2d8d6630afbd133faf0861d754 100644 (file)
@@ -29,7 +29,6 @@ function activity_get_expectedresult(){
   'values' => array( 
       '0' => array( 
           'id' => '1',
-          'source_contact_id' => '17',
           'activity_type_id' => '44',
           'subject' => 'Make-it-Happen Meeting',
           'activity_date_time' => '2011-01-01 00:00:00',
@@ -42,6 +41,7 @@ function activity_get_expectedresult(){
           'is_auto' => 0,
           'is_current_revision' => '1',
           'is_deleted' => 0,
+          'source_contact_id' => '17',
         ),
     ),
 );
index 391ea6824ec128f76fad62ce5e6b45e13c2d995b..516316bf6f5e04f9696a9c6462f9c7ee9889940e 100644 (file)
@@ -28,7 +28,6 @@ function activity_get_expectedresult(){
   'values' => array( 
       '0' => array( 
           'id' => '2',
-          'source_contact_id' => '17',
           'activity_type_id' => '44',
           'subject' => 'Make-it-Happen Meeting',
           'activity_date_time' => '2012-02-16 00:00:00',
@@ -41,6 +40,7 @@ function activity_get_expectedresult(){
           'is_auto' => 0,
           'is_current_revision' => '1',
           'is_deleted' => 0,
+          'source_contact_id' => '17',
         ),
     ),
 );
index 3566e9a623f2a79f7333672af406848ce6e01805..aba8da82d0a3d9a41a6aa902b0d360ac5b8fca04 100644 (file)
@@ -37,7 +37,6 @@ function activity__expectedresult(){
   'values' => array( 
       '1' => array( 
           'id' => '1',
-          'source_contact_id' => '17',
           'source_record_id' => '',
           'activity_type_id' => '1',
           'subject' => 'Make-it-Happen Meeting',
index 41d946a38eb9d4e486d0f026afd1c0e0f15f6354..48f705eeb7ec984d4ad2705f84538b94c166e940 100644 (file)
@@ -32,7 +32,6 @@ function activity_get_expectedresult(){
   'values' => array( 
       '0' => array( 
           'id' => '1',
-          'source_contact_id' => '17',
           'activity_type_id' => '44',
           'subject' => 'test activity type id',
           'activity_date_time' => '2011-06-02 14:36:13',
@@ -48,8 +47,12 @@ function activity_get_expectedresult(){
           'assignee_contact_id' => array( 
               '0' => '19',
             ),
+          'source_contact_id' => '17',
           'api.contact.get' => array( 
               'is_error' => 0,
+              'undefined_fields' => array( 
+                  '0' => 'api.has_parent',
+                ),
               'version' => 3,
               'count' => 1,
               'id' => 17,
@@ -83,12 +86,6 @@ function activity_get_expectedresult(){
                       'organization_name' => '',
                       'sic_code' => '',
                       'contact_is_deleted' => 0,
-                      'gender_id' => '',
-                      'gender' => '',
-                      'prefix_id' => '',
-                      'prefix' => '',
-                      'suffix_id' => '',
-                      'suffix' => '',
                       'current_employer' => '',
                       'address_id' => '',
                       'street_address' => '',
index ea2cd37f3d408dffa5f1c733b0547a982c7b8e4e..e4b48badd0f09358d486e2b119a02302ec275efa 100644 (file)
@@ -37,6 +37,12 @@ function activity_getfields_expectedresult(){
           'import' => true,
           'where' => 'civicrm_activity.activity_type_id',
           'headerPattern' => '/(activity.)?type(.id$)/i',
+          'pseudoconstant' => array( 
+              'optionGroupName' => 'activity_type',
+            ),
+          'api.aliases' => array( 
+              '0' => 'activity_type',
+            ),
         ),
       'activity_date_time' => array( 
           'name' => 'activity_date_time',
@@ -65,17 +71,11 @@ function activity_getfields_expectedresult(){
           'type' => 1,
           'title' => 'Priority',
           'pseudoconstant' => array( 
-              'name' => 'priority',
               'optionGroupName' => 'priority',
             ),
           'api.aliases' => array( 
               '0' => 'priority',
             ),
-          'options' => array( 
-              '1' => 'Urgent',
-              '2' => 'Normal',
-              '3' => 'Low',
-            ),
         ),
       'parent_id' => array( 
           'name' => 'parent_id',
@@ -181,7 +181,13 @@ function activity_getfields_expectedresult(){
           'import' => true,
           'where' => 'civicrm_activity.status_id',
           'headerPattern' => '/(activity.)?status(.label$)?/i',
+          'pseudoconstant' => array( 
+              'optionGroupName' => 'activity_status',
+            ),
           'uniqueName' => 'activity_status_id',
+          'api.aliases' => array( 
+              '0' => 'status',
+            ),
         ),
       'is_test' => array( 
           'name' => 'is_test',
@@ -198,7 +204,13 @@ function activity_getfields_expectedresult(){
           'type' => 1,
           'title' => 'Activity Medium',
           'default' => 'UL',
+          'pseudoconstant' => array( 
+              'optionGroupName' => 'encounter_medium',
+            ),
           'uniqueName' => 'activity_medium_id',
+          'api.aliases' => array( 
+              '0' => 'medium',
+            ),
         ),
       'result' => array( 
           'name' => 'result',
@@ -235,22 +247,29 @@ function activity_getfields_expectedresult(){
           'import' => true,
           'where' => 'civicrm_activity.engagement_level',
           'export' => true,
+          'pseudoconstant' => array( 
+              'optionGroupName' => 'engagement_index',
+            ),
           'uniqueName' => 'activity_engagement_level',
         ),
       'source_contact_id' => array( 
+          'name' => 'source_contact_id',
+          'title' => 'Activity Source Contact',
+          'type' => 1,
+          'FKClassName' => 'CRM_Activity_DAO_ActivityContact',
           'api.default' => 'user_contact_id',
         ),
       'assignee_contact_id' => array( 
           'name' => 'assignee_id',
           'title' => 'assigned to',
           'type' => 1,
-          'FKClassName' => 'CRM_Activity_DAO_ActivityAssignment',
+          'FKClassName' => 'CRM_Activity_DAO_ActivityContact',
         ),
       'target_contact_id' => array( 
           'name' => 'target_id',
           'title' => 'Activity Target',
           'type' => 1,
-          'FKClassName' => 'CRM_Activity_DAO_ActivityTarget',
+          'FKClassName' => 'CRM_Activity_DAO_ActivityContact',
         ),
       'activity_status_id' => array( 
           'name' => 'status_id',
index c06247340dd1e424513b9f3de27ed2febfe8ecf9..d7cfd2c5928473943ba635998bb221eb04de39d3 100644 (file)
@@ -1060,46 +1060,46 @@ function _civicrm_api3_validate_fields($entity, $action, &$params, $errorMode =
   }
   $fields = civicrm_api($entity, 'getfields', array('version' => 3, 'action' => $action));
   $fields = array_intersect_key($fields['values'], $params);
-  foreach ($fields as $fieldname => $fieldInfo) {
+  foreach ($fields as $fieldName => $fieldInfo) {
     switch (CRM_Utils_Array::value('type', $fieldInfo)) {
       case CRM_Utils_Type::T_INT:
         //field is of type integer
-        _civicrm_api3_validate_integer($params, $fieldname, $fieldInfo, $entity);
+        _civicrm_api3_validate_integer($params, $fieldName, $fieldInfo, $entity);
         break;
 
       case 4:
       case 12:
         //field is of type date or datetime
-        _civicrm_api3_validate_date($params, $fieldname, $fieldInfo);
+        _civicrm_api3_validate_date($params, $fieldName, $fieldInfo);
         break;
 
-      case 32://blob
-        _civicrm_api3_validate_html($params, $fieldname, $fieldInfo);
+    case 32://blob
+        _civicrm_api3_validate_html($params, $fieldName, $fieldInfo);
         break;
 
       case CRM_Utils_Type::T_STRING:
-        _civicrm_api3_validate_string($params, $fieldname, $fieldInfo);
+        _civicrm_api3_validate_string($params, $fieldName, $fieldInfo, $entity);
         break;
 
       case CRM_Utils_Type::T_MONEY:
-        if (!CRM_Utils_Rule::money($params[$fieldname])) {
-          throw new Exception($fieldname . " is  not a valid amount: " . $params[$fieldname]);
+        if (!CRM_Utils_Rule::money($params[$fieldName])) {
+          throw new Exception($fieldName . " is  not a valid amount: " . $params[$fieldName]);
         }
     }
 
     // intensive checks - usually only called after DB level fail
     if (!empty($errorMode) && strtolower($action) == 'create') {
       if (CRM_Utils_Array::value('FKClassName', $fieldInfo)) {
-        if (CRM_Utils_Array::value($fieldname, $params)) {
-          _civicrm_api3_validate_constraint($params, $fieldname, $fieldInfo);
+        if (CRM_Utils_Array::value($fieldName, $params)) {
+          _civicrm_api3_validate_constraint($params, $fieldName, $fieldInfo);
         }
         elseif (CRM_Utils_Array::value('required', $fieldInfo)) {
-          throw new Exception("DB Constraint Violation - possibly $fieldname should possibly be marked as mandatory for this API. If so, please raise a bug report");
+          throw new Exception("DB Constraint Violation - possibly $fieldName should possibly be marked as mandatory for this API. If so, please raise a bug report");
         }
       }
       if (CRM_Utils_Array::value('api.unique', $fieldInfo)) {
         $params['entity'] = $entity;
-        _civicrm_api3_validate_uniquekey($params, $fieldname, $fieldInfo);
+        _civicrm_api3_validate_uniquekey($params, $fieldName, $fieldInfo);
       }
     }
   }
@@ -1116,10 +1116,10 @@ function _civicrm_api3_validate_fields($entity, $action, &$params, $errorMode =
  * may not be the best thing to do. There is no code level documentation on the existing functions to work off
  *
  * @param array $params params from civicrm_api
- * @param string $fieldname uniquename of field being checked
+ * @param string $fieldName uniquename of field being checked
  * @param array $fieldinfo array of fields from getfields function
  */
-function _civicrm_api3_validate_date(&$params, &$fieldname, &$fieldInfo) {
+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 (CRM_Utils_Array::value($fieldInfo['name'], $params)) {
     //accept 'whatever strtotime accepts
@@ -1128,12 +1128,12 @@ function _civicrm_api3_validate_date(&$params, &$fieldname, &$fieldInfo) {
     }
     $params[$fieldInfo['name']] = CRM_Utils_Date::processDate($params[$fieldInfo['name']]);
   }
-  if ((CRM_Utils_Array::value('name', $fieldInfo) != $fieldname) && CRM_Utils_Array::value($fieldname, $params)) {
+  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
-    if (strtotime($params[$fieldname]) === FALSE) {
-      throw new Exception($fieldname . " is not a valid date: " . $params[$fieldname]);
+    if (strtotime($params[$fieldName]) === FALSE) {
+      throw new Exception($fieldName . " is not a valid date: " . $params[$fieldName]);
     }
-    $params[$fieldname] = CRM_Utils_Date::processDate($params[$fieldname]);
+    $params[$fieldName] = CRM_Utils_Date::processDate($params[$fieldName]);
   }
 }
 
@@ -1141,16 +1141,16 @@ function _civicrm_api3_validate_date(&$params, &$fieldname, &$fieldInfo) {
  * Validate foreign constraint fields being passed into API.
  *
  * @param array $params params from civicrm_api
- * @param string $fieldname uniquename of field being checked
+ * @param string $fieldName uniquename of field being checked
  * @param array $fieldinfo array of fields from getfields function
  */
-function _civicrm_api3_validate_constraint(&$params, &$fieldname, &$fieldInfo) {
+function _civicrm_api3_validate_constraint(&$params, &$fieldName, &$fieldInfo) {
   $dao = new $fieldInfo['FKClassName'];
-  $dao->id = $params[$fieldname];
+  $dao->id = $params[$fieldName];
   $dao->selectAdd();
   $dao->selectAdd('id');
   if (!$dao->find()) {
-    throw new Exception("$fieldname is not valid : " . $params[$fieldname]);
+    throw new Exception("$fieldName is not valid : " . $params[$fieldName]);
   }
 }
 
@@ -1158,18 +1158,18 @@ function _civicrm_api3_validate_constraint(&$params, &$fieldname, &$fieldInfo) {
  * Validate foreign constraint fields being passed into API.
  *
  * @param array $params params from civicrm_api
- * @param string $fieldname uniquename of field being checked
+ * @param string $fieldName uniquename of field being checked
  * @param array $fieldinfo array of fields from getfields function
  */
-function _civicrm_api3_validate_uniquekey(&$params, &$fieldname, &$fieldInfo) {
+function _civicrm_api3_validate_uniquekey(&$params, &$fieldName, &$fieldInfo) {
   $existing = civicrm_api($params['entity'], 'get', array(
       'version' => $params['version'],
-      $fieldname => $params[$fieldname],
+      $fieldName => $params[$fieldName],
     ));
   // an entry already exists for this unique field
   if ($existing['count'] == 1) {
     // question - could this ever be a security issue?
-    throw new Exception("Field: `$fieldname` must be unique. An conflicting entity already exists - id: " . $existing['id']);
+    throw new Exception("Field: `$fieldName` must be unique. An conflicting entity already exists - id: " . $existing['id']);
   }
 }
 
@@ -1437,59 +1437,37 @@ function _civicrm_api3_swap_out_aliases(&$apiRequest) {
  * It currently converts the incoming value 'user_contact_id' into the id of the currenty logged in user
  *
  * @param array $params params from civicrm_api
- * @param string $fieldname uniquename of field being checked
+ * @param string $fieldName uniquename of field being checked
  * @param array $fieldinfo array of fields from getfields function
  */
-function _civicrm_api3_validate_integer(&$params, &$fieldname, &$fieldInfo, $entity) {
+function _civicrm_api3_validate_integer(&$params, &$fieldName, &$fieldInfo, $entity) {
   //if fieldname exists in params
-  if (CRM_Utils_Array::value($fieldname, $params)) {
+  if (CRM_Utils_Array::value($fieldName, $params)) {
     //if value = 'user_contact_id' replace value with logged in user id
-    if ($params[$fieldname] == "user_contact_id") {
+    if ($params[$fieldName] == "user_contact_id") {
       $session = &CRM_Core_Session::singleton();
-      $params[$fieldname] = $session->get('userID');
+      $params[$fieldName] = $session->get('userID');
     }
-    if (!empty($fieldInfo['options'])) {
-      $constant = CRM_Utils_Array::value('options', $fieldInfo);
-      if (is_numeric($params[$fieldname]) && !CRM_Utils_Array::value('FKClassName',$fieldInfo) && !array_key_exists($params[$fieldname], $fieldInfo['options'])) {
-        throw new API_Exception("$fieldname is not valid", 2001, array('error_field' => $fieldname,"type"=>"integer"));
-      }
-    }
-    // we are looking for strings that should be swapped out e.g swap 'Donation' to financial_type_id 1
-    if (!is_numeric($params[$fieldname]) && !is_array($params[$fieldname])) {
-      if(CRM_Utils_Array::value('FKClassName', $fieldInfo)){
-        // we'll get the options for this now since we are doing a swap out
-        $options = civicrm_api($entity, 'getoptions', array('version' => 3, 'field' => $fieldname));
-        if(empty($options['is_error'])){
-          $fieldInfo['options'] = $options['values'];
-        }
-      }
-      if(!empty($fieldInfo['options'])){
-        $numericvalue = array_search($params[$fieldname], $fieldInfo['options']);
-        if (empty($numericvalue)) {
-          throw new Exception("$fieldname " . $params[$fieldname] . " is not valid");
-        }
-        else {
-          $params[$fieldname] = $numericvalue;
-        }
-      }
+    if (!empty($fieldInfo['pseudoconstant'])) {
+      _civicrm_api3_api_match_pseudoconstant($params, $entity, $fieldName, 'integer');
     }
 
     // once we have done any swaps check our field length
-    if(is_string($params[$fieldname]) &&
+    if(is_string($params[$fieldName]) &&
       CRM_Utils_Array::value('maxlength',$fieldInfo)
-      && strlen($params[$fieldname]) > $fieldInfo['maxlength']
+      && strlen($params[$fieldName]) > $fieldInfo['maxlength']
       ){
-      throw new API_Exception( $params[$fieldname] . " is " . strlen($params[$fieldname]) . " characters  - longer than $fieldname length" . $fieldInfo['maxlength'] . ' characters',
-        2100, array('field' => $fieldname, "max_length"=>$fieldInfo['maxlength'])
+      throw new API_Exception( $params[$fieldName] . " is " . strlen($params[$fieldName]) . " characters  - longer than $fieldName length" . $fieldInfo['maxlength'] . ' characters',
+        2100, array('field' => $fieldName, "max_length"=>$fieldInfo['maxlength'])
       );
     }
   }
 }
 
-function _civicrm_api3_validate_html(&$params, &$fieldname, &$fieldInfo) {
-  if ($value = CRM_Utils_Array::value($fieldname, $params)) {
+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"));
+      throw new API_Exception('Illegal characters in input (potential scripting attack)',array("field"=>$fieldName,"error_code"=>"xss"));
     }
   }
 }
@@ -1497,12 +1475,12 @@ function _civicrm_api3_validate_html(&$params, &$fieldname, &$fieldInfo) {
 /**
  * Validate string fields being passed into API.
  * @param array $params params from civicrm_api
- * @param string $fieldname uniquename of field being checked
+ * @param string $fieldName uniquename of field being checked
  * @param array $fieldinfo array of fields from getfields function
  */
-function _civicrm_api3_validate_string(&$params, &$fieldname, &$fieldInfo) {
+function _civicrm_api3_validate_string(&$params, &$fieldName, &$fieldInfo, $entity) {
   // If fieldname exists in params
-  $value = CRM_Utils_Array::value($fieldname, $params, '');
+  $value = CRM_Utils_Array::value($fieldName, $params, '');
   if(!is_array($value)){
     $value = (string) $value;
   }
@@ -1515,28 +1493,108 @@ function _civicrm_api3_validate_string(&$params, &$fieldname, &$fieldInfo) {
     if (!CRM_Utils_Rule::xssString($value)) {
       throw new Exception('Illegal characters in input (potential scripting attack)');
     }
-    if ($fieldname == 'currency') {
+    if ($fieldName == 'currency') {
       if (!CRM_Utils_Rule::currencyCode($value)) {
         throw new Exception("Currency not a valid code: $value");
       }
     }
-    if (!empty ($fieldInfo['options'])) {
-      // Validate & swap out any pseudoconstants / options
-      $options = $fieldInfo['options'];
-      $lowerCaseOptions = array_map("strtolower", $options);
-      // If value passed is not a key, it may be a label
-      // Try to lookup key from label - if it can't be found throw error
-      if (!is_array($value) && !isset($options[strtolower($value)]) && !isset($options[$value]) ) {
-        if (!in_array(strtolower($value), $lowerCaseOptions)) {
-          throw new Exception("$fieldname `$value` is not valid.");
-        }
-      }
+    if (!empty($fieldInfo['pseudoconstant'])) {
+      _civicrm_api3_api_match_pseudoconstant($params, $entity, $fieldName, 'string');
     }
     // 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.",
-        2100, array('field' => $fieldname)
+      throw new API_Exception("Value for $fieldName is " . strlen($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: entity field name
+ * @param $type: field type from metadata
+ */
+function _civicrm_api3_api_match_pseudoconstant(&$params, $entity, $fieldName, $type) {
+  $options = civicrm_api($entity, 'getoptions', array('version' => 3, 'field' => $fieldName));
+  $options = CRM_Utils_Array::value('values', $options, array());
+
+  // If passed a value-seperated string, explode to an array, then re-implode after matching values
+  $implode = FALSE;
+  if (is_string($params[$fieldName]) && strpos($params[$fieldName], CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) {
+    $params[$fieldName] = CRM_Utils_Array::explodePadded($params[$fieldName]);
+    $implode = TRUE;
+  }
+  // If passed multiple options, validate each
+  if (is_array($params[$fieldName])) {
+    foreach ($params[$fieldName] as &$value) {
+      _civicrm_api3_api_match_pseudoconstant_value($value, $options, $fieldName);
+    }
+    // TODO: unwrap the call to implodePadded from the conditional and do it always
+    // need to verify that this is safe and doesn't break anything though.
+    // Better yet would be to leave it as an array and ensure that every dao/bao can handle array input
+    if ($implode) {
+      CRM_Utils_Array::implodePadded($params[$fieldName]);
+    }
+  }
+  else {
+    _civicrm_api3_api_match_pseudoconstant_value($params[$fieldName], $options, $fieldName);
+  }
+}
+
+/**
+ * Validate & swap a single option value for a field
+ *
+ * @param $value: field value
+ * @param $options: array of options for this field
+ * @param $fieldName: entity field name
+ */
+function _civicrm_api3_api_match_pseudoconstant_value(&$value, $options, $fieldName) {
+  // If option is a key, no need to translate
+  if (isset($options[$value])) {
+    return;
+  }
+  // Generate error msg before overwriting the value
+  $errorMsg = "'$value' is not a valid option for field $fieldName";
+
+  // Case-insensitive matching
+  $value = strtolower($value);
+  $options = array_map("strtolower", $options);
+  $value = array_search($value, $options);
+  if ($value === FALSE) {
+    throw new Exception($errorMsg);
+  }
+}
+
+/**
+ * Returns the canonical name of a field
+ * @param $entity: api entity
+ * @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
+ */
+function _civicrm_api3_api_resolve_alias($entity, $fieldName) {
+  $result = civicrm_api($entity, 'getfields', array(
+    'version' => 3,
+    'action' => 'create',
+  ));
+  $meta = $result['values'];
+  if (isset($meta[$fieldName])) {
+    return $meta[$fieldName]['name'];
+  }
+  if ($fieldName == "{$entity}_id") {
+    return 'id';
+  }
+  foreach ($meta as $info) {
+    if ($fieldName == CRM_Utils_Array::value('uniqueName', $info)) {
+      return $info['name'];
+    }
+    if (array_search($fieldName, CRM_Utils_Array::value('api.aliases', $info, array())) !== FALSE) {
+      return $info['name'];
+    }
+  }
+  return FALSE;
+}
index 7c97d40375008dd82aff8f86b72773a22e2943d6..e8b26cbe8034a263535c844ff9bbc880c3a47d8b 100644 (file)
@@ -309,8 +309,7 @@ class api_v3_ActivityTest extends CiviUnitTestCase {
 
     $result = civicrm_api('activity', 'create', $params);
     $this->assertEquals($result['is_error'], 1, "In line " . __LINE__);
-    $this->assertEquals('priority_id is not valid', $result['error_message']);
-    $this->assertEquals(2001,$result['error_code']);
+    $this->assertEquals("'44' is not a valid option for field priority_id", $result['error_message']);
     $this->assertEquals('priority_id',$result['error_field']);
   }
 
@@ -349,7 +348,7 @@ class api_v3_ActivityTest extends CiviUnitTestCase {
 
     $result = civicrm_api('activity', 'create', $params);
     $this->assertEquals($result['is_error'], 1, "In line " . __LINE__);
-    $this->assertEquals('priority_id ergUrgent is not valid', $result['error_message']);
+    $this->assertEquals("'ergUrgent' is not a valid option for field priority_id", $result['error_message']);
   }
 
   /**