[REF] Duplicate function to allow us to work it out of the code
authorEileen McNaughton <emcnaughton@wikimedia.org>
Wed, 15 Dec 2021 03:23:11 +0000 (16:23 +1300)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Wed, 15 Dec 2021 04:18:23 +0000 (17:18 +1300)
CRM/Dedupe/Merger.php

index a34962d8d11b376bdb7c37eb8b6428ee7c0379a1..575aea7564668edc3af05349309929f0d62c40ff 100644 (file)
@@ -687,10 +687,410 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
     // This parameter causes blank fields to be be emptied out.
     // We can probably remove.
     $params['updateBlankLocInfo'] = TRUE;
-    [$data] = CRM_Contact_BAO_Contact::formatProfileContactParams($params, [], $contactID);
+    [$data] = self::formatProfileContactParams($params, [], $contactID);
     CRM_Contact_BAO_Contact::create($data);
   }
 
+  /**
+   * Format profile contact parameters.
+   *
+   * Note this function has been duplicated from CRM_Contact_BAO_Contact
+   * in order to allow us to unravel all the work this class
+   * does to prepare to call this & create some sanity. Also start to
+   * eliminate a toxic function.
+   *
+   * @param array $params
+   * @param $fields
+   * @param int $contactID
+   * @param int $ufGroupId
+   * @param null $ctype
+   * @param bool $skipCustom
+   *
+   * @return array
+   */
+  private static function formatProfileContactParams(
+    &$params,
+    $fields,
+    $contactID = NULL,
+    $ufGroupId = NULL,
+    $ctype = NULL,
+    $skipCustom = FALSE
+  ) {
+
+    $data = $contactDetails = [];
+
+    // get the contact details (hier)
+    if ($contactID) {
+      $details = CRM_Contact_BAO_Contact::getHierContactDetails($contactID, $fields);
+
+      $contactDetails = $details[$contactID];
+      $data['contact_type'] = $contactDetails['contact_type'] ?? NULL;
+      $data['contact_sub_type'] = $contactDetails['contact_sub_type'] ?? NULL;
+    }
+    else {
+      //we should get contact type only if contact
+      if ($ufGroupId) {
+        $data['contact_type'] = CRM_Core_BAO_UFField::getProfileType($ufGroupId, TRUE, FALSE, TRUE);
+
+        //special case to handle profile with only contact fields
+        if ($data['contact_type'] == 'Contact') {
+          $data['contact_type'] = 'Individual';
+        }
+        elseif (CRM_Contact_BAO_ContactType::isaSubType($data['contact_type'])) {
+          $data['contact_type'] = CRM_Contact_BAO_ContactType::getBasicType($data['contact_type']);
+        }
+      }
+      elseif ($ctype) {
+        $data['contact_type'] = $ctype;
+      }
+      else {
+        $data['contact_type'] = 'Individual';
+      }
+    }
+
+    //fix contact sub type CRM-5125
+    if (array_key_exists('contact_sub_type', $params) &&
+      !empty($params['contact_sub_type'])
+    ) {
+      $data['contact_sub_type'] = CRM_Utils_Array::implodePadded($params['contact_sub_type']);
+    }
+    elseif (array_key_exists('contact_sub_type_hidden', $params) &&
+      !empty($params['contact_sub_type_hidden'])
+    ) {
+      // if profile was used, and had any subtype, we obtain it from there
+      //CRM-13596 - add to existing contact types, rather than overwriting
+      if (empty($data['contact_sub_type'])) {
+        // If we don't have a contact ID the $data['contact_sub_type'] will not be defined...
+        $data['contact_sub_type'] = CRM_Utils_Array::implodePadded($params['contact_sub_type_hidden']);
+      }
+      else {
+        $data_contact_sub_type_arr = CRM_Utils_Array::explodePadded($data['contact_sub_type']);
+        if (!in_array($params['contact_sub_type_hidden'], $data_contact_sub_type_arr)) {
+          //CRM-20517 - make sure contact_sub_type gets the correct delimiters
+          $data['contact_sub_type'] = trim($data['contact_sub_type'], CRM_Core_DAO::VALUE_SEPARATOR);
+          $data['contact_sub_type'] = CRM_Core_DAO::VALUE_SEPARATOR . $data['contact_sub_type'] . CRM_Utils_Array::implodePadded($params['contact_sub_type_hidden']);
+        }
+      }
+    }
+
+    if ($ctype == 'Organization') {
+      $data['organization_name'] = $contactDetails['organization_name'] ?? NULL;
+    }
+    elseif ($ctype == 'Household') {
+      $data['household_name'] = $contactDetails['household_name'] ?? NULL;
+    }
+
+    $locationType = [];
+    $count = 1;
+
+    if ($contactID) {
+      //add contact id
+      $data['contact_id'] = $contactID;
+      $primaryLocationType = CRM_Contact_BAO_Contact::getPrimaryLocationType($contactID);
+    }
+    else {
+      $defaultLocation = CRM_Core_BAO_LocationType::getDefault();
+      $defaultLocationId = $defaultLocation->id;
+    }
+
+    $billingLocationTypeId = CRM_Core_BAO_LocationType::getBilling();
+
+    $blocks = ['email', 'phone', 'im', 'openid'];
+
+    $multiplFields = ['url'];
+    // prevent overwritten of formatted array, reset all block from
+    // params if it is not in valid format (since import pass valid format)
+    foreach ($blocks as $blk) {
+      if (array_key_exists($blk, $params) &&
+        !is_array($params[$blk])
+      ) {
+        unset($params[$blk]);
+      }
+    }
+
+    $primaryPhoneLoc = NULL;
+    $session = CRM_Core_Session::singleton();
+    foreach ($params as $key => $value) {
+      [$fieldName, $locTypeId, $typeId] = CRM_Utils_System::explode('-', $key, 3);
+
+      if ($locTypeId == 'Primary') {
+        if ($contactID) {
+          if (in_array($fieldName, $blocks)) {
+            $locTypeId = CRM_Contact_BAO_Contact::getPrimaryLocationType($contactID, FALSE, $fieldName);
+          }
+          else {
+            $locTypeId = CRM_Contact_BAO_Contact::getPrimaryLocationType($contactID, FALSE, 'address');
+          }
+          $primaryLocationType = $locTypeId;
+        }
+        else {
+          $locTypeId = $defaultLocationId;
+        }
+      }
+
+      if (is_numeric($locTypeId) &&
+        !in_array($fieldName, $multiplFields) &&
+        substr($fieldName, 0, 7) != 'custom_'
+      ) {
+        $index = $locTypeId;
+
+        if (is_numeric($typeId)) {
+          $index .= '-' . $typeId;
+        }
+        if (!in_array($index, $locationType)) {
+          $locationType[$count] = $index;
+          $count++;
+        }
+
+        $loc = CRM_Utils_Array::key($index, $locationType);
+
+        $blockName = self::getLocationEntityForKey($fieldName);
+
+        $data[$blockName][$loc]['location_type_id'] = $locTypeId;
+
+        //set is_billing true, for location type "Billing"
+        if ($locTypeId == $billingLocationTypeId) {
+          $data[$blockName][$loc]['is_billing'] = 1;
+        }
+
+        if ($contactID) {
+          //get the primary location type
+          if ($locTypeId == $primaryLocationType) {
+            $data[$blockName][$loc]['is_primary'] = 1;
+          }
+        }
+        elseif ($locTypeId == $defaultLocationId) {
+          $data[$blockName][$loc]['is_primary'] = 1;
+        }
+
+        if (in_array($fieldName, ['phone'])) {
+          if ($typeId) {
+            $data['phone'][$loc]['phone_type_id'] = $typeId;
+          }
+          else {
+            $data['phone'][$loc]['phone_type_id'] = '';
+          }
+          $data['phone'][$loc]['phone'] = $value;
+
+          //special case to handle primary phone with different phone types
+          // in this case we make first phone type as primary
+          if (isset($data['phone'][$loc]['is_primary']) && !$primaryPhoneLoc) {
+            $primaryPhoneLoc = $loc;
+          }
+
+          if ($loc != $primaryPhoneLoc) {
+            unset($data['phone'][$loc]['is_primary']);
+          }
+        }
+        elseif ($fieldName == 'email') {
+          $data['email'][$loc]['email'] = $value;
+          if (empty($contactID)) {
+            $data['email'][$loc]['is_primary'] = 1;
+          }
+        }
+        elseif ($fieldName == 'im') {
+          if (isset($params[$key . '-provider_id'])) {
+            $data['im'][$loc]['provider_id'] = $params[$key . '-provider_id'];
+          }
+          if (strpos($key, '-provider_id') !== FALSE) {
+            $data['im'][$loc]['provider_id'] = $params[$key];
+          }
+          else {
+            $data['im'][$loc]['name'] = $value;
+          }
+        }
+        elseif ($fieldName == 'openid') {
+          $data['openid'][$loc]['openid'] = $value;
+        }
+        else {
+          if ($fieldName === 'state_province') {
+            // CRM-3393
+            if (is_numeric($value) && ((int ) $value) >= 1000) {
+              $data['address'][$loc]['state_province_id'] = $value;
+            }
+            elseif (empty($value)) {
+              $data['address'][$loc]['state_province_id'] = '';
+            }
+            else {
+              $data['address'][$loc]['state_province'] = $value;
+            }
+          }
+          elseif ($fieldName === 'country') {
+            // CRM-3393
+            if (is_numeric($value) && ((int ) $value) >= 1000
+            ) {
+              $data['address'][$loc]['country_id'] = $value;
+            }
+            elseif (empty($value)) {
+              $data['address'][$loc]['country_id'] = '';
+            }
+            else {
+              $data['address'][$loc]['country'] = $value;
+            }
+          }
+          elseif ($fieldName === 'county') {
+            $data['address'][$loc]['county_id'] = $value;
+          }
+          elseif ($fieldName == 'address_name') {
+            $data['address'][$loc]['name'] = $value;
+          }
+          elseif (substr($fieldName, 0, 14) === 'address_custom') {
+            $data['address'][$loc][substr($fieldName, 8)] = $value;
+          }
+          else {
+            $data[$blockName][$loc][$fieldName] = $value;
+          }
+        }
+      }
+      else {
+        if (substr($key, 0, 4) === 'url-') {
+          $websiteField = explode('-', $key);
+          $data['website'][$websiteField[1]]['website_type_id'] = $websiteField[1];
+          $data['website'][$websiteField[1]]['url'] = $value;
+        }
+        elseif (in_array($key, CRM_Contact_BAO_Contact::$_greetingTypes, TRUE)) {
+          //save email/postal greeting and addressee values if any, CRM-4575
+          $data[$key . '_id'] = $value;
+        }
+        elseif (!$skipCustom && ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key))) {
+          // for autocomplete transfer hidden value instead of label
+          if ($params[$key] && isset($params[$key . '_id'])) {
+            $value = $params[$key . '_id'];
+          }
+
+          // we need to append time with date
+          if ($params[$key] && isset($params[$key . '_time'])) {
+            $value .= ' ' . $params[$key . '_time'];
+          }
+
+          // if auth source is not checksum / login && $value is blank, do not proceed - CRM-10128
+          if (($session->get('authSrc') & (CRM_Core_Permission::AUTH_SRC_CHECKSUM + CRM_Core_Permission::AUTH_SRC_LOGIN)) == 0 &&
+            ($value == '' || !isset($value))
+          ) {
+            continue;
+          }
+
+          $valueId = NULL;
+          if (!empty($params['customRecordValues'])) {
+            if (is_array($params['customRecordValues']) && !empty($params['customRecordValues'])) {
+              foreach ($params['customRecordValues'] as $recId => $customFields) {
+                if (is_array($customFields) && !empty($customFields)) {
+                  foreach ($customFields as $customFieldName) {
+                    if ($customFieldName == $key) {
+                      $valueId = $recId;
+                      break;
+                    }
+                  }
+                }
+              }
+            }
+          }
+
+          //CRM-13596 - check for contact_sub_type_hidden first
+          if (array_key_exists('contact_sub_type_hidden', $params)) {
+            $type = $params['contact_sub_type_hidden'];
+          }
+          else {
+            $type = $data['contact_type'];
+            if (!empty($data['contact_sub_type'])) {
+              $type = CRM_Utils_Array::explodePadded($data['contact_sub_type']);
+            }
+          }
+
+          CRM_Core_BAO_CustomField::formatCustomField($customFieldId,
+            $data['custom'],
+            $value,
+            $type,
+            $valueId,
+            $contactID,
+            FALSE,
+            FALSE
+          );
+        }
+        elseif ($key === 'edit') {
+          continue;
+        }
+        else {
+          if ($key === 'location') {
+            foreach ($value as $locationTypeId => $field) {
+              foreach ($field as $block => $val) {
+                if ($block === 'address' && array_key_exists('address_name', $val)) {
+                  $value[$locationTypeId][$block]['name'] = $value[$locationTypeId][$block]['address_name'];
+                }
+              }
+            }
+          }
+          if ($key === 'phone' && isset($params['phone_ext'])) {
+            $data[$key] = $value;
+            foreach ($value as $cnt => $phoneBlock) {
+              if ($params[$key][$cnt]['location_type_id'] == $params['phone_ext'][$cnt]['location_type_id']) {
+                $data[$key][$cnt]['phone_ext'] = CRM_Utils_Array::retrieveValueRecursive($params['phone_ext'][$cnt], 'phone_ext');
+              }
+            }
+          }
+          elseif (in_array($key, ['nick_name', 'job_title', 'middle_name', 'birth_date', 'gender_id', 'current_employer', 'prefix_id', 'suffix_id'])
+            && ($value == '' || !isset($value)) &&
+            ($session->get('authSrc') & (CRM_Core_Permission::AUTH_SRC_CHECKSUM + CRM_Core_Permission::AUTH_SRC_LOGIN)) == 0 ||
+            ($key === 'current_employer' && empty($params['current_employer']))) {
+            // CRM-10128: if auth source is not checksum / login && $value is blank, do not fill $data with empty value
+            // to avoid update with empty values
+            continue;
+          }
+          else {
+            $data[$key] = $value;
+          }
+        }
+      }
+    }
+
+    if (!isset($data['contact_type'])) {
+      $data['contact_type'] = 'Individual';
+    }
+
+    //set the values for checkboxes (do_not_email, do_not_mail, do_not_trade, do_not_phone)
+    $privacy = CRM_Core_SelectValues::privacy();
+    foreach ($privacy as $key => $value) {
+      if (array_key_exists($key, $fields)) {
+        // do not reset values for existing contacts, if fields are added to a profile
+        if (array_key_exists($key, $params)) {
+          $data[$key] = $params[$key];
+          if (empty($params[$key])) {
+            $data[$key] = 0;
+          }
+        }
+        elseif (!$contactID) {
+          $data[$key] = 0;
+        }
+      }
+    }
+
+    return [$data, $contactDetails];
+  }
+
+  /**
+   * Get the relevant location entity for the array key.
+   *
+   * This function is duplicated from CRM_Contact_BAO_Contact to allow cleanup.
+   * See self::formatProfileContactParams
+   *
+   * Based on the field name we determine which location entity
+   * we are dealing with. Apart from a few specific ones they
+   * are mostly 'address' (the default).
+   *
+   * @param string $fieldName
+   *
+   * @return string
+   */
+  protected static function getLocationEntityForKey($fieldName) {
+    if (in_array($fieldName, ['email', 'phone', 'im', 'openid'])) {
+      return $fieldName;
+    }
+    if ($fieldName === 'phone_ext') {
+      return 'phone';
+    }
+    return 'address';
+  }
+
   /**
    * Load all non-empty fields for the contacts
    *