Merge pull request #2423 from eileenmcnaughton/CRM-14139-static
[civicrm-core.git] / api / v3 / utils.php
index b0b2900b1ebdaaeddd6b6b814997b8bcbc3b46e8..0f489960cd871567eebf7f1352ac8bc39fdf8518 100644 (file)
@@ -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;
       }
     }
@@ -159,7 +161,7 @@ function civicrm_api3_create_error($msg, $data = array(), &$dao = NULL) {
   if(isset($data['sql']) && CRM_Core_Permission::check('Administer CiviCRM')) {
     $data['debug_information'] = $data['sql'];
   }
-  if (is_array($dao) && isset($dao['params']) && is_array($dao['params']) && CRM_Utils_Array::value('api.has_parent', $dao['params'])) {
+  if (is_array($dao) && isset($dao['params']) && is_array($dao['params']) && !empty($dao['params']['api.has_parent'])) {
     $errorCode = empty($data['error_code']) ? 'chained_api_failed' : $data['error_code'];
     throw new API_Exception('Error in call to ' . $dao['entity'] . '_' . $dao['action'] . ' : ' . $msg, $errorCode, $data);
   }
@@ -280,22 +282,39 @@ function _civicrm_api3_get_DAO($name) {
     // len ('civicrm_api3_') == 13
     $name = substr($name, 13, $last - 13);
   }
+  
+  $name = _civicrm_api_get_camel_name($name, 3);
 
-  if (strtolower($name) == 'individual' || strtolower($name) == 'household' || strtolower($name) == 'organization') {
+  if ($name == 'Individual' || $name == 'Household' || $name == 'Organization') {
     $name = 'Contact';
   }
 
-  //hack to deal with incorrectly named BAO/DAO - see CRM-10859 -
-  // several of these have been removed but am not confident mailing_recipients is
-  // tests so have not tackled.
-  // correct approach for im is unclear
-  if($name == 'mailing_recipients' || $name == 'MailingRecipients'){
-    return 'CRM_Mailing_BAO_Recipients';
+  // hack to deal with incorrectly named BAO/DAO - see CRM-10859
+
+  // FIXME: DAO should be renamed CRM_Mailing_DAO_MailingRecipients
+  // but am not confident mailing_recipients is tested so have not tackled.
+  if ($name == 'MailingRecipients') {
+    return 'CRM_Mailing_DAO_Recipients';
+  }
+  // FIXME: DAO should be renamed CRM_Mailing_DAO_MailingComponent
+  if ($name == 'MailingComponent') {
+    return 'CRM_Mailing_DAO_Component';
+  }
+  // FIXME: DAO should be renamed CRM_ACL_DAO_AclRole
+  if ($name == 'AclRole') {
+    return 'CRM_ACL_DAO_EntityRole';
+  }
+  // FIXME: DAO should be renamed CRM_SMS_DAO_SmsProvider
+  // But this would impact SMS extensions so need to coordinate
+  // Probably best approach is to migrate them to use the api and decouple them from core BAOs
+  if ($name == 'SmsProvider') {
+    return 'CRM_SMS_DAO_Provider';
   }
-  if(strtolower($name) == 'im'){
-    return 'CRM_Core_BAO_IM';
+  // FIXME: DAO names should follow CamelCase convention
+  if ($name == 'Im' || $name == 'Acl') {
+    $name = strtoupper($name);
   }
-  return CRM_Core_DAO_AllCoreTables::getFullName(_civicrm_api_get_camel_name($name, 3));
+  return CRM_Core_DAO_AllCoreTables::getFullName($name);
 }
 
 /**
@@ -306,9 +325,17 @@ function _civicrm_api3_get_DAO($name) {
  * @return mixed
  */
 function _civicrm_api3_get_BAO($name) {
+  // FIXME: DAO should be renamed CRM_Badge_DAO_BadgeLayout
+  if ($name == 'PrintLabel') {
+    return 'CRM_Badge_BAO_Layout';
+  }
   $dao = _civicrm_api3_get_DAO($name);
+  if (!$dao) {
+    return NULL;
+  }
   $bao = str_replace("DAO", "BAO", $dao);
   $file = strtr($bao, '_', '/') . '.php';
+  // Check if this entity actually has a BAO. Fall back on the DAO if not.
   return stream_resolve_include_path($file) ? $bao : $dao;
 }
 
@@ -448,7 +475,7 @@ function _civicrm_api3_get_using_query_object($entity, $params, $additional_opti
     }
 
   }
-  $skipPermissions = CRM_Utils_Array::value('check_permissions', $params)? 0 :1;
+  $skipPermissions = !empty($params['check_permissions']) ? 0 :1;
 
   list($entities, $options) = CRM_Contact_BAO_Query::apiQuery(
     $newParams,
@@ -628,7 +655,7 @@ function _civicrm_api3_get_options_from_params(&$params, $queryObject = FALSE, $
     }
   }
   if ($entity && $action =='get') {
-    if (CRM_Utils_Array::value('id',$returnProperties)) {
+    if (!empty($returnProperties['id'])) {
       $returnProperties[$entity . '_id'] = 1;
       unset($returnProperties['id']);
     }
@@ -707,7 +734,7 @@ function _civicrm_api3_apply_options_to_dao(&$params, &$dao, $entity) {
 function _civicrm_api3_build_fields_array(&$bao, $unique = TRUE) {
   $fields = $bao->fields();
   if ($unique) {
-    if(!CRM_Utils_Array::value('id', $fields)){
+    if (empty($fields['id'])){
      $entity = _civicrm_api_get_entity_name_from_dao($bao);
      $fields['id'] = $fields[$entity . '_id'];
      unset($fields[$entity . '_id']);
@@ -750,7 +777,7 @@ function _civicrm_api3_get_unique_name_array(&$bao) {
  */
 function _civicrm_api3_dao_to_array($dao, $params = NULL, $uniqueFields = TRUE, $entity = "") {
   $result = array();
-  if(isset($params['options']) && CRM_Utils_Array::value('is_count', $params['options'])) {
+  if(isset($params['options']) && !empty($params['options']['is_count'])) {
     return $dao->count();
   }
   if (empty($dao) || !$dao->find()) {
@@ -761,7 +788,7 @@ function _civicrm_api3_dao_to_array($dao, $params = NULL, $uniqueFields = TRUE,
     return $dao->count;
   }
   //if custom fields are required we will endeavour to set them . NB passing $entity in might be a bit clunky / unrequired
-  if (!empty($entity) && CRM_Utils_Array::value('return', $params) && is_array($params['return'])) {
+  if (!empty($entity) && !empty($params['return']) && is_array($params['return'])) {
     foreach ($params['return'] as $return) {
       if (substr($return, 0, 6) == 'custom') {
         $custom = TRUE;
@@ -885,7 +912,7 @@ function _civicrm_api3_check_required_fields($params, $daoName, $return = FALSE)
       continue;
     }
 
-    if (CRM_Utils_Array::value('required', $v)) {
+    if (!empty($v['required'])) {
       // 0 is a valid input for numbers, CRM-8122
       if (!isset($params[$k]) || (empty($params[$k]) && !($params[$k] === 0))) {
         $missing[] = $k;
@@ -911,7 +938,7 @@ function _civicrm_api3_check_required_fields($params, $daoName, $return = FALSE)
  * @param $entity string API entity being accessed
  * @param $action string API action being performed
  * @param $params array  params of the API call
- * @param $throw bool    whether to throw exception instead of returning false
+ * @param $throw deprecated bool    whether to throw exception instead of returning false
  *
  * @throws Exception
  * @return bool whether the current API user has the permission to make the call
@@ -930,16 +957,20 @@ function _civicrm_api3_api_check_permission($entity, $action, &$params, $throw =
     return TRUE;
   }
 
-  foreach ($permissions as $perm) {
-    if (!CRM_Core_Permission::check($perm)) {
-      if ($throw) {
-        throw new Exception("API permission check failed for $entity/$action call; missing permission: $perm.");
-      }
-      else {
-        return FALSE;
+  if (!CRM_Core_Permission::check($permissions)) {
+    if ($throw) {
+      if(is_array($permissions)) {
+        $permissions = implode(' and ', $permissions);
       }
+      throw new Exception("API permission check failed for $entity/$action call; insufficient permission: require $permissions");
+    }
+    else {
+      //@todo remove this - this is an internal api function called with $throw set to TRUE. It is only called with false
+      // in tests & that should be tidied up
+      return FALSE;
     }
   }
+
   return TRUE;
 }
 
@@ -997,6 +1028,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]);
@@ -1017,7 +1058,11 @@ function _civicrm_api3_basic_create($bao_name, &$params, $entity = NULL) {
  * @return CRM_Core_DAO|NULL an instance of the BAO
  */
 function _civicrm_api3_basic_create_fallback($bao_name, &$params) {
-  $entityName = CRM_Core_DAO_AllCoreTables::getBriefName(get_parent_class($bao_name));
+  $dao_name = get_parent_class($bao_name);
+  if ($dao_name === 'CRM_Core_DAO' || !$dao_name) {
+    $dao_name = $bao_name;
+  }
+  $entityName = CRM_Core_DAO_AllCoreTables::getBriefName($dao_name);
   if (empty($entityName)) {
     throw new API_Exception("Class \"$bao_name\" does not map to an entity name", "unmapped_class_to_entity", array(
       'class_name' => $bao_name,
@@ -1026,7 +1071,7 @@ function _civicrm_api3_basic_create_fallback($bao_name, &$params) {
   $hook = empty($params['id']) ? 'create' : 'edit';
 
   CRM_Utils_Hook::pre($hook, $entityName, CRM_Utils_Array::value('id', $params), $params);
-  $instance = new $bao_name();
+  $instance = new $dao_name();
   $instance->copyValues($params);
   $instance->save();
   CRM_Utils_Hook::post($hook, $entityName, $instance->id, $instance);
@@ -1150,15 +1195,15 @@ function _civicrm_api3_validate_fields($entity, $action, &$params, $fields, $err
 
     // 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)) {
+      if (!empty($fieldInfo['FKClassName'])) {
+        if (!empty($params[$fieldName])) {
           _civicrm_api3_validate_constraint($params, $fieldName, $fieldInfo);
         }
-        elseif (CRM_Utils_Array::value('required', $fieldInfo)) {
+        elseif (!empty($fieldInfo['required'])) {
           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)) {
+      if (!empty($fieldInfo['api.unique'])) {
         $params['entity'] = $entity;
         _civicrm_api3_validate_uniquekey($params, $fieldName, $fieldInfo);
       }
@@ -1184,14 +1229,14 @@ function _civicrm_api3_validate_fields($entity, $action, &$params, $fields, $err
  */
 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)) {
+  if (!empty($params[$fieldInfo['name']])) {
     //accept 'whatever strtotime accepts
     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']]);
   }
-  if ((CRM_Utils_Array::value('name', $fieldInfo) != $fieldName) && CRM_Utils_Array::value($fieldName, $params)) {
+  if ((CRM_Utils_Array::value('name', $fieldInfo) != $fieldName) && !empty($params[$fieldName])) {
     //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]);
@@ -1458,7 +1503,7 @@ function _civicrm_api3_getrequired($apiRequest, $fields) {
   $required = array('version');
 
   foreach ($fields as $field => $values) {
-    if (CRM_Utils_Array::value('api.required', $values)) {
+    if (!empty($values['api.required'])) {
       $required[] = $field;
     }
   }
@@ -1474,7 +1519,7 @@ function _civicrm_api3_getrequired($apiRequest, $fields) {
 function _civicrm_api3_swap_out_aliases(&$apiRequest, $fields) {
   foreach ($fields as $field => $values) {
     $uniqueName = CRM_Utils_Array::value('uniqueName', $values);
-    if (CRM_Utils_Array::value('api.aliases', $values)) {
+    if (!empty($values['api.aliases'])) {
       // if aliased field is not set we try to use field alias
       if (!isset($apiRequest['params'][$field])) {
         foreach ($values['api.aliases'] as $alias) {
@@ -1487,9 +1532,7 @@ function _civicrm_api3_swap_out_aliases(&$apiRequest, $fields) {
         }
       }
     }
-    if (!isset($apiRequest['params'][$field])
-      && CRM_Utils_Array::value('name', $values)
-      && $field != $values['name']
+    if (!isset($apiRequest['params'][$field]) && !empty($values['name']) && $field != $values['name']
       && isset($apiRequest['params'][$values['name']])
     ) {
       $apiRequest['params'][$field] = $apiRequest['params'][$values['name']];
@@ -1521,7 +1564,7 @@ function _civicrm_api3_swap_out_aliases(&$apiRequest, $fields) {
  */
 function _civicrm_api3_validate_integer(&$params, &$fieldName, &$fieldInfo, $entity) {
   //if fieldname exists in params
-  if (CRM_Utils_Array::value($fieldName, $params)) {
+  if (!empty($params[$fieldName])) {
     // if value = 'user_contact_id' (or similar), replace value with contact id
     if (!is_numeric($params[$fieldName]) && is_scalar($params[$fieldName])) {
       $realContactId = _civicrm_api3_resolve_contactID($params[$fieldName]);
@@ -1543,9 +1586,7 @@ function _civicrm_api3_validate_integer(&$params, &$fieldName, &$fieldInfo, $ent
     }
 
     // Check our field length
-    if(is_string($params[$fieldName]) &&
-      CRM_Utils_Array::value('maxlength',$fieldInfo)
-      && strlen($params[$fieldName]) > $fieldInfo['maxlength']
+    if(is_string($params[$fieldName]) && !empty($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'])