You are here

function wf_crm_process_submission in Webform CiviCRM Integration 7.3

Webform submission handler Create/update CiviCRM contacts and related data Called by presave, insert and update webform hooks

Parameters

$node: Node object

$submission: Webform submission object

$op: Name of hook being called

3 calls to wf_crm_process_submission()
webform_civicrm_webform_submission_insert in ./webform_civicrm.module
Implements hook_webform_submission_insert(). Final submission processing - saves activity and writes webform_civicrm record.
webform_civicrm_webform_submission_presave in ./webform_civicrm.module
Implements hook_webform_submission_presave(). Initial submission processing - saves contacts.
webform_civicrm_webform_submission_update in ./webform_civicrm.module
Implements hook_webform_submission_update(). Submission update processing - updates civicrm data and webform_civicrm record.

File

./webform_civicrm_forms.inc, line 545

Code

function wf_crm_process_submission($node, &$submission, $op) {
  civicrm_initialize();
  static $data = array();
  static $id = array();
  static $update = array();
  $settings = $node->webform_civicrm;

  // Presave processing - save contact data
  if ($op == 'presave') {
    $config = CRM_Core_Config::singleton();
    require_once 'CRM/Core/BAO/CustomValueTable.php';
    $enabled = wf_crm_enabled_fields($node);
    $fields = wf_crm_get_fields();
    $data = $settings['data'];
    $shared_address = array();

    // Retrieve stored ids
    $id = wf_crm_aval(wf_crm_storage($node->nid), 'id');

    // Fetch contact ids from "existing contact" fields
    foreach ($enabled as $field_key => $fid) {
      if (substr($field_key, -8) == 'existing') {
        list(, $c, ) = explode('_', $field_key, 3);
        $id['cid'][$c] = 0;
        $cid = wf_crm_aval(wf_crm_sub_value($submission, $fid), 0);
        if ($cid && is_numeric($cid)) {
          module_load_include('inc', 'webform_civicrm', 'contact_component');
          $component = $node->webform['components'][$fid];
          $filters = wf_crm_search_filters($node, $component);

          // Verify access to this contact
          if (wf_crm_contact_access($component, $filters, $cid) !== FALSE) {
            $id['cid'][$c] = $cid;
          }
        }
      }
    }
    $updating = $id;

    // If this is an update op, set param for drupal_write_record()
    if (!empty($submission->sid)) {
      $submitted = array(
        $submission->sid => new stdClass(),
      );
      webform_civicrm_webform_submission_load($submitted);
      if (isset($submitted[$submission->sid]->civicrm)) {
        $update = 'sid';
      }
    }

    // While saving a draft, just skip to the end and write the record
    if (!empty($submission->is_draft)) {
      return;
    }

    // Fill entity arrays with field values
    _wf_crm_form_data($data, $enabled, $submission, $updating, $node);

    // Create/update contacts
    foreach ($data['contact'] as $c => $contact) {
      $contact_type = $contact['contact'][1]['contact_type'];
      if (empty($id['cid'][$c])) {

        // Don't create contact if we don't have a name or email
        $ok = FALSE;
        foreach (wf_crm_required_contact_fields($contact_type) as $f) {
          if (!empty($contact[$f['table']][1][$f['name']])) {
            $ok = TRUE;
          }
        }
        if (!$ok) {
          $id['cid'][$c] = 0;
          continue;
        }

        // Search for an existing contact using default strict rule
        require_once 'CRM/Dedupe/Finder.php';
        $params = array(
          'check_permission' => FALSE,
        );
        foreach ($contact as $table => $field) {
          if (is_array($field) && !empty($field[1])) {
            if (substr($table, 0, 2) == 'cg') {

              //TODO pass custom data to deduper
            }
            elseif ($table == 'address' && !empty($field[1]['master_id'])) {
              $m = $field[1]['master_id'];

              // If master address is exposed to the form, use it
              if (!empty($contact[$m]['address'][1])) {
                $params['civicrm_address'] = $contact[$m]['address'][1];
              }
              elseif (!empty($updating['cid'][$m])) {
                $masters = wf_civicrm_api('address', 'get', array(
                  'contact_id' => $id['cid'][$m],
                  'sort' => 'is_primary DESC',
                ));
                if (!empty($masters['values'])) {
                  $params['civicrm_address'] = array_shift($masters['values']);
                }
              }
            }
            elseif (in_array($table, array(
              'contact',
              'address',
              'email',
              'phone',
              'website',
            ))) {
              $params['civicrm_' . $table] = $field[1];
            }
          }
        }
        if ($dupes = CRM_Dedupe_Finder::dupesByParams($params, ucfirst($contact_type))) {
          $id['cid'][$c] = $dupes[0];
        }
      }
      $params = $contact['contact'][1];

      // Current employer must wait for ContactRef ids to be filled
      unset($params['employer_id']);

      // CiviCRM API is too picky about this, imho
      $params['contact_type'] = ucfirst($contact_type);

      // Create new contact
      if (empty($id['cid'][$c])) {
        unset($params['contact_id']);
        $params['source'] = $settings['new_contact_source'];

        // If creating individual with no first/last name,
        // set display name and sort_name
        if ($contact_type == 'individual' && empty($params['first_name']) && empty($params['last_name'])) {
          $params['display_name'] = $params['sort_name'] = empty($params['nick_name']) ? $contact['email'][1]['email'] : $params['nick_name'];
        }
        $result = wf_civicrm_api('contact', 'create', $params);
        if (!empty($result['id'])) {
          $id['cid'][$c] = $result['id'];
        }
        else {
          $id['cid'][$c] = 0;
        }
      }
      else {

        // Fetch data from existing multivalued fields
        $fetch = $multi = array();
        foreach ($fields as $fid => $field) {
          if (!empty($field['extra']['multiple']) && substr($fid, 0, 7) == 'contact') {
            list(, $name) = explode('_', $fid, 2);
            if ($name != 'privacy' && isset($params[$name])) {
              $fetch["return.{$name}"] = 1;
              $multi[] = $name;
            }
          }
        }

        // Merge data from existing multivalued fields
        if ($multi) {
          $existing = wf_civicrm_api('contact', 'get', array(
            'id' => $id['cid'][$c],
          ) + $fetch);
          $existing = wf_crm_aval($existing, 'values:' . $id['cid'][$c], array());
          foreach ($multi as $name) {
            $exist = drupal_map_assoc(wf_crm_aval($existing, $name, array()));

            // Only known contacts are allowed to empty a field
            if (!empty($updating['cid'][$c])) {
              $fid = wf_crm_aval($enabled, "civicrm_{$c}_contact_1_contact_{$name}");
              foreach (wf_crm_exposed_options($node, $fid) as $k => $v) {
                unset($exist[$k]);
              }
            }
            $params[$name] = array_unique(array_merge($params[$name], $exist));
          }
        }
        $params['contact_id'] = $id['cid'][$c];
        wf_civicrm_api('contact', 'create', $params);
      }
    }

    // $id['cid'] will now contain all contact ids in order, with 0 as a placeholder for any contact not saved
    ksort($id['cid']);

    // Fill ContactRef fields with contact IDs
    _wf_crm_fill_contact_ref($data, $id['cid']);

    // Create/update other data associated with contacts
    foreach ($data['contact'] as $c => $contact) {
      if (!($cid = $id['cid'][$c])) {
        continue;
      }

      // Save current employer
      if ($contact['contact'][1]['contact_type'] == 'individual' && !empty($contact['contact'][1]['employer_id'])) {
        wf_civicrm_api('contact', 'create', array(
          'contact_id' => $cid,
          'contact_type' => 'Individual',
          'employer_id' => $contact['contact'][1]['employer_id'],
        ));
      }

      // Save custom data
      wf_crm_save_custom($contact, $cid, 'Contact', !empty($updating['cid'][$c]));

      // Fill values for hidden ID & CS fields
      $fid = 'civicrm_' . $c . '_contact_1_contact_';
      if (!empty($enabled[$fid . 'contact_id'])) {
        wf_crm_sub_value($submission, $enabled[$fid . 'contact_id'], $cid);
      }
      if (!empty($enabled[$fid . 'existing'])) {
        wf_crm_sub_value($submission, $enabled[$fid . 'existing'], $cid);
      }
      if (!empty($enabled[$fid . 'external_identifier']) && !empty($updating['cid'][$c])) {
        $exid = wf_civicrm_api('contact', 'get', array(
          'contact_id' => $cid,
          'return.external_identifier' => 1,
        ));
        wf_crm_sub_value($submission, $enabled[$fid . 'external_identifier'], wf_crm_aval($exid, "values:{$cid}:external_identifier"));
      }
      if (!empty($enabled[$fid . 'cs'])) {
        $cs = wf_crm_sub_value($submission, $enabled[$fid . 'cs']);
        $life = !empty($cs[0]) ? intval(24 * $cs[0]) : 'inf';
        require_once 'CRM/Contact/BAO/Contact/Utils.php';
        $cs = CRM_Contact_BAO_Contact_Utils::generateChecksum($cid, NULL, $life);
        wf_crm_sub_value($submission, $enabled[$fid . 'cs'], $cs);
      }

      // Save location data
      foreach (array(
        'address',
        'email',
        'phone',
        'website',
      ) as $location) {
        if (!empty($contact[$location])) {
          $existing = array();
          $params = array(
            'contact_id' => $cid,
          );
          if ($location != 'website') {
            $params['options'] = array(
              'sort' => 'is_primary DESC',
            );
          }
          $result = wf_civicrm_api($location, 'get', $params);
          if (!empty($result['values'])) {

            // start array index at 1
            $existing = array_merge(array(
              array(),
            ), $result['values']);
          }
          foreach ($contact[$location] as $i => $params) {

            // Translate state/prov abbr to id
            if (!empty($params['state_province_id'])) {
              if (!($params['state_province_id'] = wf_crm_state_abbr($params['state_province_id'], 'id', wf_crm_aval($params, 'country_id', $config->defaultContactCountry)))) {
                $params['state_province_id'] = '';
              }
            }

            // Update drupal email address
            if ($location == 'email' && !empty($params['email']) && $i == 1) {
              $uid = wf_crm_user_cid($cid, 'contact');
              if ($uid) {
                $user = user_load($uid);
                if ($params['email'] != $user->mail) {

                  // Verify this email is unique before saving it to user
                  $args = array(
                    ':mail' => $params['email'],
                  );
                  if (!db_query("SELECT count(uid) FROM {users} WHERE mail = :mail", $args)
                    ->fetchField()) {
                    user_save($user, array(
                      'mail' => $params['email'],
                    ));
                  }
                }
              }
            }

            // Check if anything was changed, else skip the update
            if (!empty($existing[$i])) {
              $same = TRUE;
              foreach ($params as $param => $val) {
                if ($val != (string) wf_crm_aval($existing[$i], $param, '')) {
                  $same = FALSE;
                }
              }
              if ($same) {
                continue;
              }
            }
            $empty = FALSE;
            if ($location == 'address') {

              // Check if nothing was entered for address
              $empty = empty($params['street_address']) && empty($params['city']) && empty($params['state_province_id']) && empty($params['country_id']) && empty($params['postal_code']) && empty($params['master_id']);

              // Store shared addresses for later since we haven't necessarily processed
              // the contact this address is shared with yet.
              if (!empty($params['master_id'])) {
                $shared_address[$cid][$i] = array(
                  'id' => wf_crm_aval($existing, "{$i}:id"),
                  'mc' => $params['master_id'],
                  'loc' => $params['location_type_id'],
                );
                continue;
              }

              // Reset calculated values when updating an address
              $params['master_id'] = $params['geo_code_1'] = $params['geo_code_2'] = 'null';
            }
            elseif ($location == 'website') {
              $empty = empty($params['url']);
            }
            elseif (empty($params[$location])) {
              $empty = TRUE;
            }
            $params['contact_id'] = $cid;
            if (!empty($existing[$i])) {
              $params['id'] = $existing[$i]['id'];
            }
            if ($empty) {

              // Delete this location if nothing was entered and this is a known contact
              if (!empty($updating['cid'][$c]) && !empty($params['id'])) {
                wf_civicrm_api($location, 'delete', $params);
              }
              continue;
            }
            if ($location != 'website') {
              if (empty($params['location_type_id'])) {
                $params['location_type_id'] = wf_crm_aval($existing, "{$i}:location_type_id", 1);
              }
              $params['is_primary'] = $i == 1 ? 1 : 0;
            }
            $result = wf_civicrm_api($location, 'create', $params);
            if ($location == 'address' && empty($result['is_error'])) {

              // Process custom data for address
              $custom = array();
              foreach ($params as $param => $val) {
                if (strpos($param, 'custom_') !== FALSE) {
                  $custom[$param] = $val;
                }
              }
              if ($custom) {
                $custom['entityID'] = $result['id'];
                CRM_Core_BAO_CustomValueTable::setValues($custom);
              }
            }
          }
        }
      }

      // Process relationships
      if (!empty($contact['relationship'])) {
        foreach ($contact['relationship'] as $i => $params) {
          if (!empty($params['relationship_type_id']) && !empty($id['cid'][$i])) {
            wf_crm_process_relationship($params, $cid, $id['cid'][$i]);
          }
        }
      }

      // Process groups & tags
      foreach ($fields as $fid => $field) {
        list($set, $type) = explode('_', $fid, 2);
        if ($set == 'other') {
          $field_name = 'civicrm_' . $c . '_contact_1_' . $fid;
          if (!empty($contact['other'][1][$type]) || isset($enabled[$field_name])) {
            $fid = wf_crm_aval($enabled, $field_name, 0);
            $add = wf_crm_aval($contact, "other:1:{$type}", array());
            $remove = empty($updating['cid'][$c]) ? array() : wf_crm_exposed_options($node, $fid, $add);
            wf_crm_add_remove($field['table'], 'contact', $node, $cid, $add, $remove);
          }
        }
      }

      // Process event participation
      if (in_array('CiviEvent', $config->enableComponents, TRUE) && !empty($data['participant_reg_type'])) {
        $n = $data['participant_reg_type'] == 'separate' ? $c : 1;
        if ($p = wf_crm_aval($data, "participant:{$n}:participant")) {

          // Fetch existing participant records
          $existing = array();
          $dao =& CRM_CORE_DAO::executeQuery("SELECT id, event_id FROM civicrm_participant WHERE contact_id = {$cid} AND is_test = 0");
          while ($dao
            ->fetch()) {
            $existing[$dao->event_id] = $dao->id;
          }
          foreach ($p as $e => $params) {
            $remove = array();
            if ($fid = wf_crm_aval($enabled, 'civicrm_' . $c . '_participant_' . $e . '_participant_event_id')) {
              foreach (wf_crm_exposed_options($node, $fid) as $eid => $title) {
                list($eid) = explode('-', $eid);
                if (isset($existing[$eid])) {
                  $remove[$eid] = $title;
                }
              }
            }
            if (!empty($params['event_id'])) {
              $params['contact_id'] = $cid;
              if (empty($params['campaign_id']) || !in_array('CiviCampaign', $config->enableComponents, TRUE)) {
                unset($params['campaign_id']);
              }

              // Reformat custom data from nested arrays
              $custom = array();
              foreach ($data['participant'][$n] as $key => $vals) {
                if (substr($key, 0, 2) == 'cg' && isset($vals[$e])) {
                  $custom[$key][1] = $vals[$e];
                }
              }

              // Loop through event ids to support multi-valued form elements
              $events = (array) $params['event_id'];
              foreach ($events as $i => $eid) {
                if (!empty($eid)) {
                  list($eid) = explode('-', $eid);
                  $params['event_id'] = $eid;
                  unset($remove[$eid], $params['registered_by_id'], $params['id'], $params['source']);

                  // Is existing participant?
                  if (!empty($existing[$eid])) {
                    $params['id'] = $existing[$params['event_id']];
                  }
                  else {
                    $params['source'] = check_plain($node->title);
                    if ($c > 1 && !empty($registered_by_id[$e][$i])) {
                      $params['registered_by_id'] = $registered_by_id[$e][$i];
                    }
                  }
                  $result = wf_civicrm_api('participant', 'create', $params);

                  // When registering contact 1, store id to apply to other contacts
                  if ($c == 1) {
                    $registered_by_id[$e][$i] = $result['id'];
                  }
                  if ($custom) {
                    wf_crm_save_custom($custom, $result['id'], 'Participant');
                  }
                }
              }
            }
            foreach ($remove as $eid => $title) {
              wf_civicrm_api('participant', 'create', array(
                'status_id' => 4,
                'id' => $existing[$eid],
              ));
              drupal_set_message(t('Registration canceled for !event', array(
                '!event' => $title,
              )));
            }
          }
        }
      }
    }

    // Process shared addresses. We do this last after all contacts and addresses exist.
    foreach ($shared_address as $cid => $shared) {
      foreach ($shared as $i => $addr) {
        if (!empty($id['cid'][$addr['mc']])) {
          $masters = wf_civicrm_api('address', 'get', array(
            'contact_id' => $id['cid'][$addr['mc']],
            'options' => array(
              'sort' => 'is_primary DESC',
            ),
          ));
          if (!empty($masters['values'])) {
            $masters = array_values($masters['values']);

            // Pick the address with the same location type; default to primary.
            $params = $masters[0];
            foreach ($masters as $m) {
              if ($m['location_type_id'] == $addr['loc']) {
                $params = $m;
                break;
              }
            }
            $params['master_id'] = $params['id'];
            $params['id'] = $addr['id'];
            $params['contact_id'] = $cid;
            $params['is_primary'] = $i == 1;
            wf_civicrm_api('address', 'create', $params);
          }
        }
      }
    }
  }
  elseif (empty($submission->is_draft)) {
    $config = CRM_Core_Config::singleton();

    // Process case
    if (!empty($data['case'][1]['case'][1]['client_id'])) {
      if (empty($id['case'][1])) {

        // Search for case
        $case = wf_crm_case_find($data['case'][1]);
        if ($case) {
          $id['case'][1] = $case['id'];
        }
      }

      // Default subject
      if (empty($id['case'][1]) && empty($data['case'][1]['case'][1]['subject'])) {
        $data['case'][1]['case'][1]['subject'] = check_plain($node->title);
      }
      if (!empty($id['case'][1])) {
        $data['case'][1]['case'][1]['id'] = $id['case'][1];
        unset($data['case'][1]['case'][1]['creator_id'], $data['case'][1]['case'][1]['case_type_id']);
      }

      // Save case
      $result = wf_civicrm_api('case', 'create', $data['case'][1]['case'][1]);

      // Legacy API cruft - TODO: remove these 3 lines when dropping Civi 4.2 support
      if (!empty($result['values']['id']) && empty($result['id'])) {
        $result['id'] = $result['values']['id'];
      }
      if (!empty($result['id'])) {
        wf_crm_save_custom($data['case'][1], $result['id'], 'Case');
        $id['case'][1] = $result['id'];
      }
    }

    // Process activity
    if (!empty($data['activity'][1]['activity'])) {

      // Search for activity
      if (!empty($id['cid'][1]) && empty($data['case'][1]) || !empty($data['case'][1]) && !empty($id['case'][1])) {
        $params = array(
          'activity_type_id' => $data['activity'][1]['activity'][1]['activity_type_id'],
          'status_id' => $data['activity'][1]['existing_activity_status'],
        );
        if (!empty($data['case'][1])) {
          $params['case_id'] = $id['case'][1];
        }
        else {
          $params['contact_id'] = $id['cid'][1];
        }
        if (empty($id['act'][1])) {
          $id['act'][1] = wf_crm_activity_find($params);
        }
      }

      // Save activity
      $params = $data['activity'][1]['activity'][1];
      if (is_array($params['status_id'])) {
        $params['status_id'] = reset($params['status_id']);
      }

      // Existing activity - set id
      if (!empty($id['act'][1])) {
        $params['id'] = $id['act'][1];
      }
      else {
        if (!empty($id['case'][1])) {
          $params['case_id'] = $id['case'][1];
          $params['medium_id'] = $data['case'][1]['case'][1]['medium_id'];
        }
        $cid = wf_crm_user_cid();
        if ($cid) {
          $params['source_contact_id'] = $cid;
        }
        else {
          foreach ($id['cid'] as $cid) {
            if ($cid) {
              $params['source_contact_id'] = $cid;
              break;
            }
          }
        }
      }

      // Can't pass an empty contact id to the api
      if (empty($params['assignee_contact_id'])) {
        unset($params['assignee_contact_id']);
      }

      // Format details as html
      $params['details'] = nl2br(wf_crm_aval($params, 'details', ''));
      if (empty($params['subject'])) {
        $params['subject'] = $data['activity'][1]['activity'][1]['subject'];
      }
      if (!empty($data['activity'][1]['details']['entire_result'])) {
        module_load_include('inc', 'webform', 'includes/webform.submissions');
        $params['details'] .= webform_submission_render($node, $submission, NULL, 'html');
      }
      if (!empty($data['activity'][1]['details']['view_link'])) {
        $params['details'] .= '<p>' . l(t('View Webform Submission'), 'node/' . $node->nid . '/submission/' . $submission->sid, array(
          'absolute' => TRUE,
          'alias' => TRUE,
        )) . '</p>';
      }
      if (!empty($data['activity'][1]['details']['edit_link'])) {
        $params['details'] .= '<p>' . l(t('Edit Submission'), 'node/' . $node->nid . '/submission/' . $submission->sid . '/edit', array(
          'absolute' => TRUE,
          'alias' => TRUE,
        )) . '</p>';
      }
      if (empty($params['campaign_id']) || !in_array('CiviCampaign', $config->enableComponents, TRUE)) {
        unset($params['campaign_id']);
      }
      if (!empty($data['activity'][1]['activity'][1]['survey_id'])) {
        $params['source_record_id'] = $data['activity'][1]['activity'][1]['survey_id'];
      }
      $result = wf_civicrm_api('activity', 'create', $params);
      if (!empty($result['id'])) {
        $id['act'][1] = $result['id'];
        wf_crm_save_custom($data['activity'][1], $id['act'][1], 'Activity');
      }
      if (!empty($id['act'][1]) && !empty($params['assignee_contact_id'])) {
        require_once 'CRM/Core/BAO/Setting.php';
        if (CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'activity_assignee_notification')) {

          // Send email to assignees. TODO: Move to CiviCRM API?
          $assignee = wf_civicrm_api('contact', 'get', array(
            'id' => $params['assignee_contact_id'],
          ));
          $assignee = wf_crm_aval($assignee, 'values:' . $params['assignee_contact_id']);
          if (!empty($assignee['email'])) {
            $mail = array(
              $assignee['email'] => $assignee,
            );

            // Include attachments while sendig a copy of activity.
            require_once 'CRM/Core/BAO/File.php';
            $attachments =& CRM_Core_BAO_File::getEntityFile('civicrm_activity', $id['act'][1]);
            require_once 'CRM/Case/BAO/Case.php';
            CRM_Case_BAO_Case::sendActivityCopy(NULL, $id['act'][1], $mail, $attachments, NULL);
          }
        }
      }
    }
  }

  // Write record; we do this when creating, updating, or saving a draft of a webform submission.
  if ($op != 'presave') {
    $cid = '-';
    foreach (array_keys($data['contact']) as $c) {
      $cid .= (empty($id['cid'][$c]) ? 0 : $id['cid'][$c]) . '-';
    }
    $record = array(
      'sid' => $submission->sid,
      'contact_id' => $cid,
      'activity_id' => empty($id['act'][1]) ? 0 : $id['act'][1],
    );
    drupal_write_record('webform_civicrm_submissions', $record, $update);
  }
}