Merge remote-tracking branch 'upstream/4.5' into 4.5-4.6-2015-03-16-17-24-33
[civicrm-core.git] / CRM / Core / BAO / CustomField.php
index 883db2ae36ed2570047c15d31022c860c275e088..432bd3d27d0b3fa44d093e1e08be63da0bc59565 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
   +--------------------------------------------------------------------+
-  | CiviCRM version 4.5                                                |
+  | CiviCRM version 4.6                                                |
   +--------------------------------------------------------------------+
   | Copyright CiviCRM LLC (c) 2004-2014                                |
   +--------------------------------------------------------------------+
@@ -23,7 +23,7 @@
   | GNU Affero General Public License or the licensing of CiviCRM,     |
   | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
   +--------------------------------------------------------------------+
-*/
+ */
 
 /**
  *
@@ -43,7 +43,6 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
    * Array for valid combinations of data_type & descriptions
    *
    * @var array
-   * @static
    */
   public static $_dataType = NULL;
 
@@ -51,7 +50,6 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
    * Array for valid combinations of data_type & html_type
    *
    * @var array
-   * @static
    */
   public static $_dataToHtml = NULL;
 
@@ -59,20 +57,16 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
    * Array to hold (formatted) fields for import
    *
    * @var array
-   * @static
    */
   public static $_importFields = NULL;
 
   /**
-   * Build and retrieve the list of data types and descriptions
-   *
-   * @param NULL
+   * Build and retrieve the list of data types and descriptions.
    *
-   * @return array        Data type => Description
-   * @access public
-   * @static
+   * @return array
+   *   Data type => Description
    */
-  static function &dataType() {
+  public static function &dataType() {
     if (!(self::$_dataType)) {
       self::$_dataType = array(
         'String' => ts('Alphanumeric'),
@@ -95,12 +89,14 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   /**
    * @return array
    */
-  static function dataToHtml() {
+  public static function dataToHtml() {
     if (!self::$_dataToHtml) {
       self::$_dataToHtml = array(
         array(
-          'Text' => 'Text', 'Select' => 'Select',
-          'Radio' => 'Radio', 'CheckBox' => 'CheckBox',
+          'Text' => 'Text',
+          'Select' => 'Select',
+          'Radio' => 'Radio',
+          'CheckBox' => 'CheckBox',
           'Multi-Select' => 'Multi-Select',
           'AdvMulti-Select' => 'AdvMulti-Select',
           'Autocomplete-Select' => 'Autocomplete-Select',
@@ -122,17 +118,16 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   }
 
   /**
-   * takes an associative array and creates a custom field object
+   * Takes an associative array and creates a custom field object.
    *
    * This function is invoked from within the web form layer and also from the api layer
    *
-   * @param array $params (reference) an assoc array of name/value pairs
+   * @param array $params
+   *   (reference) an assoc array of name/value pairs.
    *
-   * @return CRM_Core_DAO_CustomField object
-   * @access public
-   * @static
+   * @return CRM_Core_DAO_CustomField
    */
-  static function create(&$params) {
+  public static function create(&$params) {
     $origParams = array_merge(array(), $params);
 
     if (!isset($params['id'])) {
@@ -158,13 +153,14 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
       $indexExist = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', $params['id'], 'is_searchable');
     }
 
-    switch (CRM_Utils_Array::value('html_type', $params)){
+    switch (CRM_Utils_Array::value('html_type', $params)) {
       case 'Select Date':
-        if(empty($params['date_format'])){
+        if (empty($params['date_format'])) {
           $config = CRM_Core_Config::singleton();
           $params['date_format'] = $config->dateInputFormat;
         }
         break;
+
       case 'CheckBox':
       case 'AdvMulti-Select':
       case 'Multi-Select':
@@ -188,14 +184,18 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
             $params['default_value'] = $params['option_value'][$params['default_option']];
           }
         }
-       break;
+        break;
     }
 
     $transaction = new CRM_Core_Transaction();
     // create any option group & values if required
     if ($params['html_type'] != 'Text' &&
       in_array($params['data_type'], array(
-        'String', 'Int', 'Float', 'Money'))
+        'String',
+        'Int',
+        'Float',
+        'Money',
+      ))
     ) {
 
       $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup',
@@ -203,8 +203,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
         'table_name'
       );
 
-
-      if ($params['option_type'] == 1) {
+      if ($params['option_type'] == 1 && empty($params['option_group_id'])) {
         // first create an option group for this custom group
         $optionGroup = new CRM_Core_DAO_OptionGroup();
         $optionGroup->name = "{$columnName}_" . date('YmdHis');
@@ -212,7 +211,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
         $optionGroup->is_active = 1;
         $optionGroup->save();
         $params['option_group_id'] = $optionGroup->id;
-          if(!empty($params['option_value']) && is_array($params['option_value'])){
+        if (!empty($params['option_value']) && is_array($params['option_value'])) {
           foreach ($params['option_value'] as $k => $v) {
             if (strlen(trim($v))) {
               $optionValue = new CRM_Core_DAO_OptionValue();
@@ -260,8 +259,9 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
         );
 
         if (!CRM_Utils_System::isNull(explode(CRM_Core_DAO::VALUE_SEPARATOR,
-              $defaultValue
-            ))) {
+          $defaultValue
+        ))
+        ) {
           $params['default_value'] = $defaultValue;
         }
       }
@@ -278,7 +278,8 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     $customField->is_searchable = CRM_Utils_Array::value('is_searchable', $params, FALSE);
     $customField->in_selector = CRM_Utils_Array::value('in_selector', $params, FALSE);
     $customField->is_search_range = CRM_Utils_Array::value('is_search_range', $params, FALSE);
-    $customField->is_active = CRM_Utils_Array::value('is_active', $params, FALSE);
+    //CRM-15792 - Custom field gets disabled if is_active not set
+    $customField->is_active = CRM_Utils_Array::value('is_active', $params, TRUE);
     $customField->is_view = CRM_Utils_Array::value('is_view', $params, FALSE);
     $customField->save();
 
@@ -313,35 +314,32 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   }
 
   /**
-   * Takes a bunch of params that are needed to match certain criteria and
-   * retrieves the relevant objects. Typically the valid params are only
-   * contact_id. We'll tweak this function to be more full featured over a period
-   * of time. This is the inverse function of create. It also stores all the retrieved
-   * values in the default array
-   *
-   * @param array $params   (reference ) an assoc array of name/value pairs
-   * @param array $defaults (reference ) an assoc array to hold the flattened values
-   *
-   * @return object CRM_Core_DAO_CustomField object
-   * @access public
-   * @static
+   * Fetch object based on array of properties.
+   *
+   * @param array $params
+   *   (reference ) an assoc array of name/value pairs.
+   * @param array $defaults
+   *   (reference ) an assoc array to hold the flattened values.
+   *
+   * @return CRM_Core_DAO_CustomField
    */
-  static function retrieve(&$params, &$defaults) {
+  public static function retrieve(&$params, &$defaults) {
     return CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_CustomField', $params, $defaults);
   }
 
   /**
-   * update the is_active flag in the db
+   * Update the is_active flag in the db.
    *
-   * @param int      $id         Id of the database record
-   * @param boolean  $is_active  Value we want to set the is_active field
+   * @param int $id
+   *   Id of the database record.
+   * @param bool $is_active
+   *   Value we want to set the is_active field.
    *
-   * @return   Object            DAO object on sucess, null otherwise
+   * @return Object
+   *   DAO object on sucess, null otherwise
    *
-   * @access public
-   * @static
    */
-  static function setIsActive($id, $is_active) {
+  public static function setIsActive($id, $is_active) {
 
     CRM_Utils_System::flushCache();
 
@@ -353,12 +351,11 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   /**
    * Get the field title.
    *
-   * @param int $id id of field.
-   *
-   * @return string name
+   * @param int $id
+   *   Id of field.
    *
-   * @access public
-   * @static
+   * @return string
+   *   name
    *
    */
   public static function getTitle($id) {
@@ -368,28 +365,36 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   /**
    * Store and return an array of all active custom fields.
    *
-   * @param string      $customDataType      type of Custom Data; empty is a synonym for "all contact data types"
-   * @param boolean     $showAll             If true returns all fields (includes disabled fields)
-   * @param boolean     $inline              If true returns all inline fields (includes disabled fields)
-   * @param int         $customDataSubType   Custom Data sub type value
-   * @param int         $customDataSubName   Custom Data sub name value
-   * @param boolean     $onlyParent          return only top level custom data, for eg, only Participant and ignore subname and subtype
-   * @param boolean     $onlySubType         return only custom data for subtype
-   * @param boolean     $checkPermission     if false, do not include permissioning clause
+   * @param string $customDataType
+   *   Type of Custom Data; empty is a synonym for "all contact data types".
+   * @param bool $showAll
+   *   If true returns all fields (includes disabled fields).
+   * @param bool $inline
+   *   If true returns all inline fields (includes disabled fields).
+   * @param int $customDataSubType
+   *   Custom Data sub type value.
+   * @param int $customDataSubName
+   *   Custom Data sub name value.
+   * @param bool $onlyParent
+   *   Return only top level custom data, for eg, only Participant and ignore subname and subtype.
+   * @param bool $onlySubType
+   *   Return only custom data for subtype.
+   * @param bool $checkPermission
+   *   If false, do not include permissioning clause.
    *
-   * @return array      $fields - an array of active custom fields.
+   * @return array
+   *   an array of active custom fields.
    *
-   * @access public
-   * @static
    */
-  public static function &getFields($customDataType = 'Individual',
-                                    $showAll = FALSE,
-                                    $inline = FALSE,
-                                    $customDataSubType = NULL,
-                                    $customDataSubName = NULL,
-                                    $onlyParent = FALSE,
-                                    $onlySubType = FALSE,
-                                    $checkPermission = TRUE
+  public static function &getFields(
+    $customDataType = 'Individual',
+    $showAll = FALSE,
+    $inline = FALSE,
+    $customDataSubType = NULL,
+    $customDataSubName = NULL,
+    $onlyParent = FALSE,
+    $onlySubType = FALSE,
+    $checkPermission = TRUE
   ) {
     if (empty($customDataType)) {
       $customDataType = array('Contact', 'Individual', 'Organization', 'Household');
@@ -504,10 +509,13 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
                             $cfTable.option_group_id,
                             $cfTable.date_format,
                             $cfTable.time_format,
-                            $cgTable.is_multiple
+                            $cgTable.is_multiple,
+                            og.name as option_group_name
                      FROM $cfTable
                      INNER JOIN $cgTable
-                     ON $cfTable.custom_group_id = $cgTable.id
+                       ON $cfTable.custom_group_id = $cgTable.id
+                     LEFT JOIN civicrm_option_group og
+                       ON $cfTable.option_group_id = og.id
                      WHERE ( 1 ) ";
 
         if (!$showAll) {
@@ -572,6 +580,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
           $fields[$dao->id]['date_format'] = $dao->date_format;
           $fields[$dao->id]['time_format'] = $dao->time_format;
           $fields[$dao->id]['is_required'] = $dao->is_required;
+          self::getOptionsForField($fields[$dao->id], $dao->option_group_name);
         }
 
         CRM_Core_BAO_Cache::setItem($fields,
@@ -589,24 +598,28 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
    * Return the field ids and names (with groups) for import purpose.
    *
    * @param int|string $contactType Contact type
-   * @param boolean $showAll If true returns all fields (includes disabled fields)
-   * @param boolean $onlyParent return fields ONLY related to basic types
-   * @param boolean $search when called from search and multiple records need to be returned
-   * @param boolean $checkPermission if false, do not include permissioning clause
+   * @param bool $showAll
+   *   If true returns all fields (includes disabled fields).
+   * @param bool $onlyParent
+   *   Return fields ONLY related to basic types.
+   * @param bool $search
+   *   When called from search and multiple records need to be returned.
+   * @param bool $checkPermission
+   *   If false, do not include permissioning clause.
    *
    * @param bool $withMultiple
    *
-   * @return array   $fields -
+   * @return array
+   *
    *
-   * @access public
-   * @static
    */
-  public static function &getFieldsForImport($contactType = 'Individual',
-    $showAll         = FALSE,
-    $onlyParent      = FALSE,
-    $search          = FALSE,
+  public static function &getFieldsForImport(
+    $contactType = 'Individual',
+    $showAll = FALSE,
+    $onlyParent = FALSE,
+    $search = FALSE,
     $checkPermission = TRUE,
-    $withMultiple    = FALSE
+    $withMultiple = FALSE
   ) {
     // Note: there are situations when we want getFieldsForImport() return fields related
     // ONLY to basic contact types, but NOT subtypes. And thats where $onlyParent is helpful
@@ -658,14 +671,14 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   }
 
   /**
-   * Get the field id from an import key
+   * Get the field id from an import key.
    *
-   * @param string $key The key to parse
+   * @param string $key
+   *   The key to parse.
    *
    * @param bool $all
-   * @return int|null         The id (if exists)
-   * @access public
-   * @static
+   * @return int|null
+   *   The id (if exists)
    */
   public static function getKeyID($key, $all = FALSE) {
     $match = array();
@@ -684,20 +697,20 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   }
 
   /**
-   * Use the cache to get all values of a specific custom field
+   * Use the cache to get all values of a specific custom field.
    *
-   * @param int   $fieldID  the custom field ID
+   * @param int $fieldID
+   *   The custom field ID.
    *
-   * @return CRM_Core_DAO_CustomField $field  the field object
-   * @static
-   * public
+   * @return CRM_Core_DAO_CustomField
+   *   The field object.
    */
-  static function getFieldObject($fieldID) {
+  public static function getFieldObject($fieldID) {
     $field = new CRM_Core_DAO_CustomField();
 
     // check if we can get the field values from the system cache
-    $cacheKey    = "CRM_Core_DAO_CustomField_{$fieldID}";
-    $cache       = CRM_Utils_Cache::singleton();
+    $cacheKey = "CRM_Core_DAO_CustomField_{$fieldID}";
+    $cache = CRM_Utils_Cache::singleton();
     $fieldValues = $cache->get($cacheKey);
     if (empty($fieldValues)) {
       $field->id = $fieldID;
@@ -718,35 +731,39 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   }
 
   /**
-   * This function for building custom fields
-   *
-   * @param CRM_Core_Form $qf form object (reference)
-   * @param string $elementName name of the custom field
-   * @param $fieldId
-   * @param boolean $inactiveNeeded -deprecated
+   * This function for building custom fields.
+   *
+   * @param CRM_Core_Form $qf
+   *   Form object (reference).
+   * @param string $elementName
+   *   Name of the custom field.
+   * @param int $fieldId
+   * @param bool $inactiveNeeded
+   *   -deprecated.
    * @param bool $useRequired
-   * @param boolean $search true if used for search else false
-   * @param string $label label for custom field
+   *   True if required else false.
+   * @param bool $search
+   *   True if used for search else false.
+   * @param string $label
+   *   Label for custom field.
    *
-   * @internal param bool $userRequired true if required else false
-   * @access public
-   * @static
    */
-  public static function addQuickFormElement(&$qf,
+  public static function addQuickFormElement(
+    &$qf,
     $elementName,
     $fieldId,
     $inactiveNeeded = FALSE,
-    $useRequired    = TRUE,
-    $search         = FALSE,
-    $label          = NULL
+    $useRequired = TRUE,
+    $search = FALSE,
+    $label = NULL
   ) {
     $field = self::getFieldObject($fieldId);
     $widget = $field->html_type;
 
     // Custom field HTML should indicate group+field name
     $groupName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $field->custom_group_id);
-    $dataCrmCustomVal = $groupName.':'.$field->name;
-    $dataCrmCustomAttr = 'data-crm-custom="'.$dataCrmCustomVal.'"';
+    $dataCrmCustomVal = $groupName . ':' . $field->name;
+    $dataCrmCustomAttr = 'data-crm-custom="' . $dataCrmCustomVal . '"';
     $field->attributes .= $dataCrmCustomAttr;
 
     // Fixed for Issue CRM-2183
@@ -757,10 +774,23 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     $placeholder = $search ? ts('- any -') : ($useRequired ? ts('- select -') : ts('- none -'));
 
     // FIXME: Why are select state/country separate widget types?
-    $isSelect = (in_array($widget, array('Select', 'Multi-Select', 'Select State/Province', 'Multi-Select State/Province', 'Select Country', 'Multi-Select Country', 'AdvMulti-Select', 'CheckBox', 'Radio')));
+    $isSelect = (in_array($widget, array(
+      'Select',
+      'Multi-Select',
+      'Select State/Province',
+      'Multi-Select State/Province',
+      'Select Country',
+      'Multi-Select Country',
+      'AdvMulti-Select',
+      'CheckBox',
+      'Radio',
+    )));
 
     if ($isSelect) {
-      $options = CRM_Utils_Array::value('values', civicrm_api3('contact', 'getoptions', array('field' => "custom_$fieldId", 'context' => $search ? 'search' : 'create'), array()));
+      $options = CRM_Utils_Array::value('values', civicrm_api3('contact', 'getoptions', array(
+        'field' => "custom_$fieldId",
+        'context' => $search ? 'search' : 'create',
+      ), array()));
 
       // Consolidate widget types to simplify the below switch statement
       if ($search || ($widget !== 'AdvMulti-Select' && strpos($widget, 'Select') !== FALSE)) {
@@ -779,7 +809,8 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
       // Add data for popup link. Normally this is handled by CRM_Core_Form->addSelect
       if ($field->option_group_id && !$search && $widget == 'Select' && CRM_Core_Permission::check('administer CiviCRM')) {
         $selectAttributes += array(
-          'data-api-entity' => 'contact', // FIXME: This works because the getoptions api isn't picky about custom fields, but it's WRONG
+          'data-api-entity' => 'contact',
+          // FIXME: This works because the getoptions api isn't picky about custom fields, but it's WRONG
           'data-api-field' => 'custom_' . $field->id,
           'data-option-edit-path' => 'civicrm/admin/options/' . CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $field->option_group_id),
         );
@@ -861,18 +892,18 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
 
           $qf->addDate($elementName, $label, $required, array(
             'format' => $field->date_format,
-              'timeFormat' => $field->time_format,
-              'startOffset' => $field->start_date_years,
-              'endOffset' => $field->end_date_years,
-              'data-crm-custom' => $dataCrmCustomVal,
-            ));
+            'timeFormat' => $field->time_format,
+            'startOffset' => $field->start_date_years,
+            'endOffset' => $field->end_date_years,
+            'data-crm-custom' => $dataCrmCustomVal,
+          ));
         }
         break;
 
       case 'Radio':
         $choice = array();
         foreach ($options as $v => $l) {
-          $choice[] = $qf->createElement('radio', NULL, '', $l, (string)$v, $field->attributes);
+          $choice[] = $qf->createElement('radio', NULL, '', $l, (string) $v, $field->attributes);
         }
         $group = $qf->addGroup($choice, $elementName, $label);
         if ($useRequired && !$search) {
@@ -950,7 +981,11 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
         break;
 
       case 'RichTextEditor':
-        $attributes = array('rows' => $field->note_rows, 'cols' => $field->note_columns, 'data-crm-custom' => $dataCrmCustomVal);
+        $attributes = array(
+          'rows' => $field->note_rows,
+          'cols' => $field->note_columns,
+          'data-crm-custom' => $dataCrmCustomVal,
+        );
         if ($field->text_length) {
           $attributes['maxlength'] = $field->text_length;
         }
@@ -962,7 +997,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
         // Fixme: why is this a string in the first place??
         $attributes = array();
         if ($field->attributes) {
-          foreach(explode(' ', $field->attributes) as $at) {
+          foreach (explode(' ', $field->attributes) as $at) {
             if (strpos($at, '=')) {
               list($k, $v) = explode('=', $at);
               $attributes[$k] = trim($v, ' "');
@@ -1048,13 +1083,8 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   /**
    * Delete the Custom Field.
    *
-   * @param   object $field - the field object
-   *
-   * @return  boolean
-   *
-   * @access public
-   * @static
-   *
+   * @param object $field
+   *   The field object.
    */
   public static function deleteField($field) {
     CRM_Utils_System::flushCache();
@@ -1072,32 +1102,32 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     $field->delete();
     CRM_Core_BAO_UFField::delUFField($field->id);
     CRM_Utils_Weight::correctDuplicateWeights('CRM_Core_DAO_CustomField');
-
-    return;
   }
 
   /**
    * Given a custom field value, its id and the set of options
    * find the display value for this field
    *
-   * @param mixed $value the custom field value
-   * @param int $id the custom field id
-   * @param int $options the assoc array of option name/value pairs
+   * @param mixed $value
+   *   The custom field value.
+   * @param int $id
+   *   The custom field id.
+   * @param int $options
+   *   The assoc array of option name/value pairs.
    *
-   * @param null $contactID
-   * @param null $fieldID
+   * @param int $contactID
+   * @param int $fieldID
    *
-   * @return  string   the display value
+   * @return string
+   *   the display value
    *
-   * @static
-   * @access public
    */
-  static function getDisplayValue($value, $id, &$options, $contactID = NULL, $fieldID = NULL) {
-    $option     = &$options[$id];
+  public static function getDisplayValue($value, $id, &$options, $contactID = NULL, $fieldID = NULL) {
+    $option = &$options[$id];
     $attributes = &$option['attributes'];
-    $html_type  = $attributes['html_type'];
-    $data_type  = $attributes['data_type'];
-    $format     = CRM_Utils_Array::value('format', $attributes);
+    $html_type = $attributes['html_type'];
+    $data_type = $attributes['data_type'];
+    $format = CRM_Utils_Array::value('format', $attributes);
 
     return self::getDisplayValueCommon($value,
       $option,
@@ -1115,18 +1145,19 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
    * @param $html_type
    * @param $data_type
    * @param null $format
-   * @param null $contactID
-   * @param null $fieldID
+   * @param int $contactID
+   * @param int $fieldID
    *
    * @return array|mixed|null|string
    */
-  static function getDisplayValueCommon($value,
+  public static function getDisplayValueCommon(
+    $value,
     &$option,
     $html_type,
     $data_type,
-    $format    = NULL,
+    $format = NULL,
     $contactID = NULL,
-    $fieldID   = NULL
+    $fieldID = NULL
   ) {
     $display = $value;
 
@@ -1150,7 +1181,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
           if ($value) {
             $display = ts('Yes');
           }
-          elseif ((string)$value === '0') {
+          elseif ((string) $value === '0') {
             $display = ts('No');
           }
         }
@@ -1321,24 +1352,25 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   }
 
   /**
-   * Function to set default values for custom data used in profile
-   *
-   * @params int    $customFieldId custom field id
-   * @params string $elementName   custom field name
-   * @params array  $defaults      associated array of fields
-   * @params int    $contactId     contact id
-   * @param $customFieldId
-   * @param $elementName
-   * @param $defaults
-   * @param null $contactId
-   * @param  int $mode profile mode
-   * @param  mixed $value if passed - dont fetch value from db,
+   * Set default values for custom data used in profile.
+   *
+   * @param int $customFieldId
+   *   Custom field id.
+   * @param string $elementName
+   *   Custom field name.
+   * @param array $defaults
+   *   Associated array of fields.
+   * @param int $contactId
+   *   Contact id.
+   * @param int $mode
+   *   Profile mode.
+   * @param mixed $value
+   *   If passed - dont fetch value from db,.
    *                               just format the given value
    *
-   * @static
-   * @access public
    */
-  static function setProfileDefaults($customFieldId,
+  public static function setProfileDefaults(
+    $customFieldId,
     $elementName,
     &$defaults,
     $contactId = NULL,
@@ -1357,8 +1389,8 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     }
     else {
       if (!isset($value)) {
-        $info   = self::getTableColumnGroup($customFieldId);
-        $query  = "SELECT {$info[0]}.{$info[1]} as value FROM {$info[0]} WHERE {$info[0]}.entity_id = {$contactId}";
+        $info = self::getTableColumnGroup($customFieldId);
+        $query = "SELECT {$info[0]}.{$info[1]} as value FROM {$info[0]} WHERE {$info[0]}.entity_id = {$contactId}";
         $result = CRM_Core_DAO::executeQuery($query);
         if ($result->fetch()) {
           $value = $result->value;
@@ -1438,22 +1470,22 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   }
 
   /**
-   * @param $contactID
-   * @param $cfID
-   * @param null $fileID
+   * @param int $contactID
+   * @param int $cfID
+   * @param int $fileID
    * @param bool $absolute
    *
    * @return array
    */
   /**
-   * @param $contactID
-   * @param $cfID
-   * @param null $fileID
+   * @param int $contactID
+   * @param int $cfID
+   * @param int $fileID
    * @param bool $absolute
    *
    * @return array
    */
-  static function getFileURL($contactID, $cfID, $fileID = NULL, $absolute = FALSE, $multiRecordWhereClause = NULL) {
+  public static function getFileURL($contactID, $cfID, $fileID = NULL, $absolute = FALSE, $multiRecordWhereClause = NULL) {
     if ($contactID) {
       if (!$fileID) {
         $params = array('id' => $cfID);
@@ -1527,42 +1559,49 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
   }
 
   /**
-   * Format custom fields before inserting
-   *
-   * @param int    $customFieldId       custom field id
-   * @param array   $customFormatted     formatted array
-   * @param mix     $value               value of custom field
-   * @param string  $customFieldExtend   custom field extends
-   * @param int     $customValueId       custom option value id
-   * @param int     $entityId            entity id (contribution, membership...)
-   * @param boolean $inline              consider inline custom groups only
-   * @param boolean $checkPermission     if false, do not include permissioning clause
-   * @param boolean $includeViewOnly     if true, fields marked 'View Only' are included. Required for APIv3.
-   *
-   * @return array $customFormatted formatted custom field array
-   * @static
+   * Format custom fields before inserting.
+   *
+   * @param int $customFieldId
+   *   Custom field id.
+   * @param array $customFormatted
+   *   Formatted array.
+   * @param mix $value
+   *   Value of custom field.
+   * @param string $customFieldExtend
+   *   Custom field extends.
+   * @param int $customValueId
+   *   Custom option value id.
+   * @param int $entityId
+   *   Entity id (contribution, membership...).
+   * @param bool $inline
+   *   Consider inline custom groups only.
+   * @param bool $checkPermission
+   *   If false, do not include permissioning clause.
+   * @param bool $includeViewOnly
+   *   If true, fields marked 'View Only' are included. Required for APIv3.
+   *
+   * @return array|NULL
+   *   formatted custom field array
    */
-  static function formatCustomField($customFieldId, &$customFormatted, $value,
+  public static function formatCustomField(
+    $customFieldId, &$customFormatted, $value,
     $customFieldExtend, $customValueId = NULL,
     $entityId = NULL,
     $inline = FALSE,
     $checkPermission = TRUE,
-    $includeViewOnly = false
+    $includeViewOnly = FALSE
   ) {
     //get the custom fields for the entity
     //subtype and basic type
     $customDataSubType = NULL;
     if (is_array($customFieldExtend)) {
-      $customFieldExtend = $customFieldExtend[0];
-    }
-
-    if (in_array($customFieldExtend,
-        CRM_Contact_BAO_ContactType::subTypes()
-      )) {
       // This is the case when getFieldsForImport() requires fields
       // of subtype and its parent.CRM-5143
-      $customDataSubType = $customFieldExtend;
-      $customFieldExtend = CRM_Contact_BAO_ContactType::getBasicType($customDataSubType);
+      // CRM-16065 - Custom field set data not being saved if contact has more than one contact sub type
+      $customDataSubType = array_intersect(CRM_Contact_BAO_ContactType::subTypes(), (array) $customFieldExtend);
+      if (!empty($customDataSubType) && is_array($customDataSubType)) {
+        $customFieldExtend = array_unique(array_values(CRM_Contact_BAO_ContactType::getBasicType($customDataSubType)));
+      }
     }
 
     $customFields = CRM_Core_BAO_CustomField::getFields($customFieldExtend,
@@ -1576,12 +1615,12 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     );
 
     if (!array_key_exists($customFieldId, $customFields)) {
-      return;
+      return NULL;
     }
 
     // return if field is a 'code' field
     if (!$includeViewOnly && !empty($customFields[$customFieldId]['is_view'])) {
-      return;
+      return NULL;
     }
 
     list($tableName, $columnName, $groupID) = self::getTableColumnGroup($customFieldId);
@@ -1612,8 +1651,8 @@ SELECT id
           }
           if (!empty($selectedValues)) {
             $value = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR,
-              $selectedValues
-            ) . CRM_Core_DAO::VALUE_SEPARATOR;
+                $selectedValues
+              ) . CRM_Core_DAO::VALUE_SEPARATOR;
           }
           else {
             $value = '';
@@ -1630,8 +1669,8 @@ SELECT id
         // and you can directly use value, CRM-4385
         if (is_array($value)) {
           $value = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR,
-            array_values($value)
-          ) . CRM_Core_DAO::VALUE_SEPARATOR;
+              array_values($value)
+            ) . CRM_Core_DAO::VALUE_SEPARATOR;
         }
       }
       else {
@@ -1771,11 +1810,11 @@ SELECT $columnName
   }
 
   /**
-   * @param $params
+   * @param array $params
    *
    * @return array
    */
-  static function &defaultCustomTableSchema(&$params) {
+  public static function &defaultCustomTableSchema(&$params) {
     // add the id and extends_id
     $table = array(
       'name' => $params['name'],
@@ -1819,7 +1858,7 @@ SELECT $columnName
    * @param bool $indexExist
    * @param bool $triggerRebuild
    */
-  static function createField($field, $operation, $indexExist = FALSE, $triggerRebuild = TRUE) {
+  public static function createField($field, $operation, $indexExist = FALSE, $triggerRebuild = TRUE) {
     $tableName = CRM_Core_DAO::getFieldValue(
       'CRM_Core_DAO_CustomGroup',
       $field->custom_group_id,
@@ -1871,7 +1910,7 @@ SELECT $columnName
       $params['fk_field_name'] = 'id';
       $params['fk_attributes'] = 'ON DELETE SET NULL';
     }
-    if ($field->default_value) {
+    if (isset($field->default_value)) {
       $params['default'] = "'{$field->default_value}'";
     }
 
@@ -1879,15 +1918,17 @@ SELECT $columnName
   }
 
   /**
-   * Determine whether it would be safe to move a field
+   * Determine whether it would be safe to move a field.
    *
-   * @param int $fieldID FK to civicrm_custom_field
-   * @param int $newGroupID FK to civicrm_custom_group
+   * @param int $fieldID
+   *   FK to civicrm_custom_field.
+   * @param int $newGroupID
+   *   FK to civicrm_custom_group.
    *
-   * @return array(
-     string) or TRUE
+   * @return array
+   *   array(string) or TRUE
    */
-  static function _moveFieldValidate($fieldID, $newGroupID) {
+  public static function _moveFieldValidate($fieldID, $newGroupID) {
     $errors = array();
 
     $field = new CRM_Core_DAO_CustomField();
@@ -1945,7 +1986,8 @@ SELECT extends
 FROM   civicrm_custom_group
 WHERE  id IN ( %1, %2 )
 ";
-      $params = array(1 => array($oldGroup->id, 'Integer'),
+      $params = array(
+        1 => array($oldGroup->id, 'Integer'),
         2 => array($newGroup->id, 'Integer'),
       );
 
@@ -1965,12 +2007,14 @@ WHERE  id IN ( %1, %2 )
   /**
    * Move a custom data field from one group (table) to another
    *
-   * @param int $fieldID FK to civicrm_custom_field
-   * @param int $newGroupID FK to civicrm_custom_group
+   * @param int $fieldID
+   *   FK to civicrm_custom_field.
+   * @param int $newGroupID
+   *   FK to civicrm_custom_group.
    *
    * @return void
    */
-  static function moveField($fieldID, $newGroupID) {
+  public static function moveField($fieldID, $newGroupID) {
     $validation = self::_moveFieldValidate($fieldID, $newGroupID);
     if (TRUE !== $validation) {
       CRM_Core_Error::fatal(implode(' ', $validation));
@@ -2007,18 +2051,19 @@ WHERE  id IN ( %1, %2 )
   }
 
   /**
-   * Get the database table name and column name for a custom field
+   * Get the database table name and column name for a custom field.
    *
-   * @param int     $fieldID - the fieldID of the custom field
-   * @param boolean $force   - force the sql to be run again (primarily used for tests)
+   * @param int $fieldID
+   *   The fieldID of the custom field.
+   * @param bool $force
+   *   Force the sql to be run again (primarily used for tests).
    *
-   * @return array           - fatal is fieldID does not exists, else array of tableName, columnName
-   * @static
-   * @public
+   * @return array
+   *   fatal is fieldID does not exists, else array of tableName, columnName
    */
-  static function getTableColumnGroup($fieldID, $force = FALSE) {
-    $cacheKey    = "CRM_Core_DAO_CustomField_CustomGroup_TableColumn_{$fieldID}";
-    $cache       = CRM_Utils_Cache::singleton();
+  public static function getTableColumnGroup($fieldID, $force = FALSE) {
+    $cacheKey = "CRM_Core_DAO_CustomField_CustomGroup_TableColumn_{$fieldID}";
+    $cache = CRM_Utils_Cache::singleton();
     $fieldValues = $cache->get($cacheKey);
     if (empty($fieldValues) || $force) {
       $query = "
@@ -2041,19 +2086,17 @@ AND    cf.id = %1";
   }
 
   /**
-   * Function to get custom option groups
+   * Get custom option groups.
    *
-   * @params array $includeFieldIds ids of custom fields for which
-   * option groups must be included.
+   * @param array $includeFieldIds
+   *   Ids of custom fields for which.
+   *   option groups must be included.
    *
    * Currently this is required in the cases where option groups are to be included
    * for inactive fields : CRM-5369
    *
-   * @access public
-   *
-   * @param null $includeFieldIds
    *
-   * @return mixed $customOptionGroup@static
+   * @return mixed
    */
   public static function &customOptionGroup($includeFieldIds = NULL) {
     static $customOptionGroup = NULL;
@@ -2090,20 +2133,16 @@ INNER JOIN  civicrm_custom_field f ON ( g.id = f.option_group_id )
   }
 
   /**
-   * Function to fix orphan groups
+   * Fix orphan groups.
    *
-   * @params int $customFieldId custom field id
-   * @params int $optionGroupId option group id
-   *
-   * @access public
-   *
-   * @param $customFieldId
-   * @param $optionGroupId
+   * @param int $customFieldId
+   *   Custom field id.
+   * @param int $optionGroupId
+   *   Option group id.
    *
    * @return void
-   * @static
    */
-  static function fixOptionGroups($customFieldId, $optionGroupId) {
+  public static function fixOptionGroups($customFieldId, $optionGroupId) {
     // check if option group belongs to any custom Field else delete
     // get the current option group
     $currentOptionGroupId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField',
@@ -2121,17 +2160,15 @@ INNER JOIN  civicrm_custom_field f ON ( g.id = f.option_group_id )
   }
 
   /**
-   * Function to check if option group is related to more than one
+   * Check if option group is related to more than one
    * custom field
    *
-   * @params int $optionGroupId option group id
-   *
-   * @param $optionGroupId
+   * @param int $optionGroupId
+   *   Option group id.
    *
    * @return void
-  @static
    */
-  static function checkOptionGroup($optionGroupId) {
+  public static function checkOptionGroup($optionGroupId) {
     $query = "
 SELECT count(*)
 FROM   civicrm_custom_field
@@ -2146,12 +2183,12 @@ WHERE  option_group_id = {$optionGroupId}";
   }
 
   /**
-   * @param $optionGroupId
+   * @param int $optionGroupId
    * @param $htmlType
    *
    * @return null|string
    */
-  static function getOptionGroupDefault($optionGroupId, $htmlType) {
+  public static function getOptionGroupDefault($optionGroupId, $htmlType) {
     $query = "
 SELECT   default_value, html_type
 FROM     civicrm_custom_field
@@ -2159,8 +2196,8 @@ WHERE    option_group_id = {$optionGroupId}
 AND      default_value IS NOT NULL
 ORDER BY html_type";
 
-    $dao             = CRM_Core_DAO::executeQuery($query);
-    $defaultValue    = NULL;
+    $dao = CRM_Core_DAO::executeQuery($query);
+    $defaultValue = NULL;
     $defaultHTMLType = NULL;
     while ($dao->fetch()) {
       if ($dao->html_type == $htmlType) {
@@ -2193,15 +2230,16 @@ ORDER BY html_type";
   }
 
   /**
-   * @param $params
+   * @param array $params
    * @param $customFields
-   * @param $entityID
+   * @param int $entityID
    * @param $customFieldExtends
    * @param bool $inline
    *
    * @return array
    */
-  static function postProcess(&$params,
+  public static function postProcess(
+    &$params,
     &$customFields,
     $entityID,
     $customFieldExtends,
@@ -2241,7 +2279,7 @@ ORDER BY html_type";
    *
    * @throws Exception
    */
-  static function buildOption($field, &$options) {
+  public static function buildOption($field, &$options) {
     // Fixme - adding anything but options to the $options array is a bad idea
     // What if an option had the key 'attributes'?
     $options['attributes'] = array(
@@ -2252,12 +2290,13 @@ ORDER BY html_type";
 
     $optionGroupID = NULL;
     if (($field['html_type'] == 'CheckBox' ||
-        $field['html_type'] == 'Radio' ||
-        $field['html_type'] == 'Select' ||
-        $field['html_type'] == 'AdvMulti-Select' ||
-        $field['html_type'] == 'Multi-Select' ||
-        ($field['html_type'] == 'Autocomplete-Select' && $field['data_type'] != 'ContactReference')
-      )) {
+      $field['html_type'] == 'Radio' ||
+      $field['html_type'] == 'Select' ||
+      $field['html_type'] == 'AdvMulti-Select' ||
+      $field['html_type'] == 'Multi-Select' ||
+      ($field['html_type'] == 'Autocomplete-Select' && $field['data_type'] != 'ContactReference')
+    )
+    ) {
       if ($field['option_group_id']) {
         $optionGroupID = $field['option_group_id'];
       }
@@ -2295,7 +2334,7 @@ SELECT label, value
    *
    * @return null
    */
-  static function getCustomFieldID($fieldLabel, $groupTitle = NULL) {
+  public static function getCustomFieldID($fieldLabel, $groupTitle = NULL) {
     $params = array(1 => array($fieldLabel, 'String'));
     if ($groupTitle) {
       $params[2] = array($groupTitle, 'String');
@@ -2329,8 +2368,11 @@ WHERE      ( f.label = %1 OR f.name = %1 )
   /**
    * Given ID of a custom field, return its name as well as the name of the custom group it belongs to.
    *
+   * @param array $ids
+   *
+   * @return array
    */
-  static function getNameFromID($ids) {
+  public static function getNameFromID($ids) {
     if (is_array($ids)) {
       $ids = implode(',', $ids);
     }
@@ -2340,7 +2382,6 @@ FROM       civicrm_custom_field f
 INNER JOIN civicrm_custom_group g ON f.custom_group_id = g.id
 WHERE      f.id IN ($ids)";
 
-
     $dao = CRM_Core_DAO::executeQuery($sql);
     $result = array();
     while ($dao->fetch()) {
@@ -2357,19 +2398,19 @@ WHERE      f.id IN ($ids)";
   /**
    * Validate custom data.
    *
-   * @param array $params custom data submitted.
-   * ie array( 'custom_1' => 'validate me' );
+   * @param array $params
+   *   Custom data submitted.
+   *   ie array( 'custom_1' => 'validate me' );
    *
-   * @return array $errors validation errors.
-   * @static
+   * @return array
+   *   validation errors.
    */
-  static function validateCustomData($params) {
+  public static function validateCustomData($params) {
     $errors = array();
     if (!is_array($params) || empty($params)) {
       return $errors;
     }
 
-
     //pick up profile fields.
     $profileFields = array();
     $ufGroupId = CRM_Utils_Array::value('ufGroupId', $params);
@@ -2396,8 +2437,8 @@ WHERE      f.id IN ($ids)";
       $dataType = $field->data_type;
 
       $profileField = CRM_Utils_Array::value($key, $profileFields, array());
-      $fieldTitle   = CRM_Utils_Array::value('title', $profileField);
-      $isRequired   = CRM_Utils_Array::value('is_required', $profileField);
+      $fieldTitle = CRM_Utils_Array::value('title', $profileField);
+      $isRequired = CRM_Utils_Array::value('is_required', $profileField);
       if (!$fieldTitle) {
         $fieldTitle = $field->label;
       }
@@ -2461,11 +2502,11 @@ WHERE      f.id IN ($ids)";
   }
 
   /**
-   * @param $customId
+   * @param int $customId
    *
    * @return bool
    */
-  static function isMultiRecordField($customId) {
+  public static function isMultiRecordField($customId) {
     $isMultipleWithGid = FALSE;
     if (!is_numeric($customId)) {
       $customId = self::getKeyID($customId);
@@ -2493,11 +2534,44 @@ WHERE cf.id = %1 AND cg.is_multiple = 1";
    * @param CRM_Core_DAO_CustomField|array $field
    * @return bool
    */
-  static function isSerialized($field) {
+  public static function isSerialized($field) {
     // Fields retrieved via api are an array, or from the dao are an object. We'll accept either.
     $field = (array) $field;
     // FIXME: Currently the only way to know if data is serialized is by looking at the html_type. It would be cleaner to decouple this.
     return ($field['html_type'] == 'CheckBox' || strpos($field['html_type'], 'Multi') !== FALSE);
   }
-}
 
+  /**
+   * @param array $field
+   * @param string|null $optionGroupName
+   */
+  private static function getOptionsForField(&$field, $optionGroupName) {
+    if ($optionGroupName) {
+      $field['pseudoconstant'] = array(
+        'optionGroupName' => $optionGroupName,
+        'optionEditPath' => 'civicrm/admin/options/' . $optionGroupName,
+      );
+    }
+    elseif ($field['data_type'] == 'Boolean') {
+      $field['pseudoconstant'] = array(
+        'callback' => 'CRM_Core_SelectValues::boolean',
+      );
+    }
+    elseif ($field['data_type'] == 'Country') {
+      $field['pseudoconstant'] = array(
+        'table' => 'civicrm_country',
+        'keyColumn' => 'id',
+        'labelColumn' => 'name',
+        'nameColumn' => 'iso_code',
+      );
+    }
+    elseif ($field['data_type'] == 'StateProvince') {
+      $field['pseudoconstant'] = array(
+        'table' => 'civicrm_state_province',
+        'keyColumn' => 'id',
+        'labelColumn' => 'name',
+      );
+    }
+  }
+
+}