You are here

commerce_addressbook.checkout_pane.inc in Commerce Addressbook 7.3

Based on commerce/modules/customer/commerce_customer.checkout_pane.inc

File

commerce_addressbook.checkout_pane.inc
View source
<?php

/**
 * @file
 * Based on commerce/modules/customer/commerce_customer.checkout_pane.inc
 */

/**
 * Checkout pane callback: returns the customer profile pane's settings form.
 */
function commerce_addressbook_pane_settings_form($checkout_pane) {
  $form = array();

  // Extract the type of profile represented by this pane from its ID.
  $type = substr($checkout_pane['pane_id'], 17);

  // Removes 'customer_profile_'
  // Build an options array of customer profile reference fields available for
  // the data from this customer profile pane.
  $options = array();
  foreach (commerce_info_fields('commerce_customer_profile_reference', 'commerce_order') as $name => $field) {
    if ($type == $field['settings']['profile_type']) {
      $instance = field_info_instance('commerce_order', $name, 'commerce_order');
      $options[$name] = check_plain($instance['label']);
    }
  }
  $form['commerce_' . $checkout_pane['pane_id'] . '_field'] = array(
    '#type' => 'select',
    '#title' => t('Related customer profile reference field'),
    '#description' => t('Specify the customer profile reference field on the order to populate with profile data from this checkout pane.'),
    '#options' => $options,
    '#empty_value' => '',
    '#default_value' => variable_get('commerce_' . $checkout_pane['pane_id'] . '_field', ''),
    '#required' => TRUE,
  );

  // Provide the option to copy values from other profile types if they exist.
  $profile_types = commerce_customer_profile_type_options_list();
  unset($profile_types[$type]);
  if (count($profile_types)) {
    $form['commerce_' . $checkout_pane['pane_id'] . '_profile_copy'] = array(
      '#type' => 'checkbox',
      '#title' => t('Enable profile copying on this checkout pane, helping customers avoid having to enter the same address twice.'),
      '#default_value' => variable_get('commerce_' . $checkout_pane['pane_id'] . '_profile_copy', FALSE),
    );
    $form['commerce_' . $checkout_pane['pane_id'] . '_profile_copy_wrapper'] = array(
      '#type' => 'fieldset',
      '#title' => t('Profile copy options'),
      '#states' => array(
        'visible' => array(
          ':input[name="commerce_' . $checkout_pane['pane_id'] . '_profile_copy"]' => array(
            'checked' => TRUE,
          ),
        ),
      ),
    );
    $form['commerce_' . $checkout_pane['pane_id'] . '_profile_copy_wrapper']['commerce_' . $checkout_pane['pane_id'] . '_profile_copy_source'] = array(
      '#type' => 'select',
      '#title' => t('Profile to copy from'),
      '#options' => $profile_types,
      '#default_value' => variable_get('commerce_' . $checkout_pane['pane_id'] . '_profile_copy_source', NULL),
    );
    $form['commerce_' . $checkout_pane['pane_id'] . '_profile_copy_wrapper']['commerce_' . $checkout_pane['pane_id'] . '_profile_copy_default'] = array(
      '#type' => 'checkbox',
      '#title' => t('Make copying information from this profile the default action, requiring users to uncheck a box on the checkout pane to enter a different address.'),
      '#default_value' => variable_get('commerce_' . $checkout_pane['pane_id'] . '_profile_copy_default', TRUE),
    );
  }
  return $form;
}

/**
 * Checkout pane callback: returns a customer profile edit form.
 */
function commerce_addressbook_pane_checkout_form($form, &$form_state, $checkout_pane, $order) {
  global $user;

  // Ensure this include file is loaded when the form is rebuilt from the cache.
  $form_state['build_info']['files']['pane'] = drupal_get_path('module', 'commerce_addressbook') . '/commerce_addressbook.checkout_pane.inc';
  $pane_id = $checkout_pane['pane_id'];

  // Extract the type of profile represented by this pane from its ID.
  $type = substr($pane_id, 17);

  // Removes 'customer_profile_'
  $field_name = variable_get('commerce_' . $pane_id . '_field', '');

  // The specified profile reference field doesn't exist on this order type.
  // A consequence of the checkout settings applying for all order types.
  if ($field_name && !field_info_instance('commerce_order', $field_name, $order->type)) {
    return array();
  }

  // Find the referenced profile using the related reference field...
  $wrapper = entity_metadata_wrapper('commerce_order', $order);
  $profile = NULL;
  $mode = 'view';

  // If the associated order field has been set...
  if ($field_name) {
    $profile = $wrapper->{$field_name}
      ->value();
  }
  else {

    // Or try the association stored in the order's data array if no field is set.
    if (!empty($order->data['profiles'][$checkout_pane['pane_id']])) {
      $profile = commerce_customer_profile_load($order->data['profiles'][$pane_id]);
    }
  }

  // If an AJAX rebuild happened, we might have our data in form state
  if (!empty($form_state['pane_' . $pane_id])) {
    $profile = $form_state['pane_' . $pane_id]['profile'];
    $mode = $form_state['pane_' . $pane_id]['mode'];
  }
  $valid_profiles = array();
  $incomplete_profiles = array();

  // Load the active user profiles.
  if ($user->uid && ($active_profiles = commerce_addressbook_get_active_profiles($user->uid, $type))) {
    foreach ($active_profiles as $active_profile) {
      if (commerce_addressbook_profile_is_incomplete($active_profile)) {
        $incomplete_profiles[$active_profile->profile_id] = $active_profile;
      }
      else {
        $valid_profiles[$active_profile->profile_id] = $active_profile;
      }
    }
  }

  // No profile set yet. First check if one exists already.
  if (empty($profile)) {
    if (!$valid_profiles) {

      // Incomplete profiles need to go into edit mode.
      if ($incomplete_profiles) {
        $mode = 'edit';
        $profile = reset($incomplete_profiles);
      }
      else {

        // No profiles found. Create a new one.
        $profile = commerce_customer_profile_new($type, $order->uid);
        $mode = 'new';
      }
    }
    else {
      $default_profile_id = commerce_addressbook_get_default_profile_id($user->uid, $type);

      // Try to see if the default profile is in the valid profiles array.
      if (isset($valid_profiles[$default_profile_id])) {
        $profile = $valid_profiles[$default_profile_id];
      }
      else {

        // Otherwise, just select the first one.
        $profile = reset($valid_profiles);
      }
    }
  }
  $ajax_wrapper = strtr($pane_id, '_', '-') . '-ajax-wrapper';
  $pane_form = array(
    '#parents' => array(
      $pane_id,
    ),
    '#prefix' => '<div id="' . $ajax_wrapper . '">',
    '#suffix' => '</div>',
  );
  _commerce_addressbook_add_profile_copy_checkbox($pane_form, $checkout_pane, $order, $type);
  $profile_copy_enabled = FALSE;

  // If profile copying is enabled.
  if (isset($pane_form['commerce_customer_profile_copy']) && !empty($pane_form['commerce_customer_profile_copy']['#default_value'])) {
    $profile_copy_enabled = TRUE;
    $mode = 'copy';
    field_attach_form('commerce_customer_profile', $profile, $pane_form, $form_state);
    $source_profile_type_name = variable_get('commerce_' . $checkout_pane['pane_id'] . '_profile_copy_source', NULL);
    if ($source_profile_type = commerce_customer_profile_type_load($source_profile_type_name)) {
      $common_fields = array();
      foreach (field_info_instances('commerce_customer_profile', $source_profile_type['type']) as $field_name => $field) {

        // If the field exists on the destination profile then disable it.
        if (!empty($pane_form[$field_name])) {
          $langcode = $pane_form[$field_name]['#language'];
          $pane_form[$field_name][$langcode]['#access'] = FALSE;
          $common_fields[] = $field_name;
        }
      }

      // Look for a matching destination profile in the valid profiles array.
      $source_pane_id = 'pane_customer_profile_' . $source_profile_type_name;

      // Check if we have multiple valid profiles, verify that the source
      // pane is in the "view" mode.
      if (count($valid_profiles) > 1 && $common_fields && isset($form_state[$source_pane_id]) && $form_state[$source_pane_id]['mode'] == 'view') {
        $source_profile = $form_state[$source_pane_id]['profile'];

        // Loop over the valid profiles to find a matching one.
        foreach ($valid_profiles as $valid_profile) {

          // Compare the fields that are common to both profile types.
          foreach ($common_fields as $field_name) {
            if ($source_profile->{$field_name} !== $valid_profile->{$field_name}) {
              continue 2;
            }
          }

          // Update the customer profile used by this pane to use the matching
          // one.
          $profile = $valid_profile;
          break;
        }
      }
    }
  }

  // Force the edit mode if we couldn't find "valid" profiles.
  if ($mode == 'view' && !empty($profile->profile_id) && !$valid_profiles) {
    $mode = 'edit';
  }

  // Remember the current profile and mode in form state.
  $form_state['pane_' . $checkout_pane['pane_id']] = array(
    'profile' => $profile,
    'mode' => $mode,
  );

  // Add the entity context of the current cart order.
  $profile->entity_context = array(
    'entity_type' => 'commerce_order',
    'entity_id' => $order->order_id,
  );

  // When the profile copy is checked, don't show any other form elements.
  if ($profile_copy_enabled) {
    return $pane_form;
  }
  if ($mode == 'view') {
    $content = entity_view('commerce_customer_profile', array(
      $profile->profile_id => $profile,
    ), 'customer');

    // Prepare the options.
    $options = array();
    foreach ($valid_profiles as $id => $customer_profile) {
      $field_values = field_get_items('commerce_customer_profile', $customer_profile, 'commerce_customer_address');
      $options[$customer_profile->profile_id] = $field_values[0]['thoroughfare'];
    }
    $options['new_address'] = t('+ Enter a new address');
    drupal_alter('commerce_addressbook_labels', $options, $valid_profiles);
    $pane_form['profile_selection'] = array(
      '#title' => t('Select an address'),
      '#options' => $options,
      '#type' => 'select',
      '#ajax' => array(
        'callback' => 'commerce_addressbook_pane_refresh',
        'wrapper' => $ajax_wrapper,
      ),
      '#element_validate' => array(
        'commerce_addressbook_saved_addresses_validate',
      ),
      '#default_value' => $profile->profile_id,
    );
    $pane_form['rendered_profile'] = array(
      '#markup' => drupal_render($content),
    );
    $pane_form['edit_button'] = array(
      '#type' => 'submit',
      '#name' => 'pane-' . $pane_id . '-edit',
      '#value' => t('Edit'),
      '#limit_validation_errors' => array(),
      '#ajax' => array(
        'callback' => 'commerce_addressbook_pane_refresh',
        'wrapper' => $ajax_wrapper,
      ),
      '#submit' => array(
        'commerce_addressbook_pane_edit',
      ),
    );
  }
  else {

    // Add the field widgets for the profile.
    field_attach_form('commerce_customer_profile', $profile, $pane_form, $form_state);

    // Tweak the form to remove the fieldset from the address field if there
    // is only one on this profile.
    $addressfields = array();
    foreach (commerce_info_fields('addressfield', 'commerce_customer_profile') as $field_name => $field) {
      if (!empty($pane_form[$field_name]['#language'])) {
        $langcode = $pane_form[$field_name]['#language'];

        // Only consider this addressfield if it's represented on the form.
        if (!empty($pane_form[$field_name][$langcode])) {
          $addressfields[] = array(
            $field_name,
            $langcode,
          );
        }
      }
    }

    // Check to ensure only one addressfield was found on the form.
    if (count($addressfields) == 1) {
      list($field_name, $langcode) = array_shift($addressfields);
      foreach (element_children($pane_form[$field_name][$langcode]) as $delta) {

        // Don't mess with the "Add another item" button that could be present.
        if ($pane_form[$field_name][$langcode][$delta]['#type'] != 'submit') {
          $pane_form[$field_name][$langcode][$delta]['#type'] = 'container';
        }
      }
    }
    if ($valid_profiles) {
      $pane_form['cancel_button'] = array(
        '#type' => 'submit',
        '#name' => 'pane-' . $pane_id . '-cancel',
        '#value' => t('Return to address selection'),
        '#limit_validation_errors' => array(),
        '#ajax' => array(
          'callback' => 'commerce_addressbook_pane_refresh',
          'wrapper' => $ajax_wrapper,
        ),
        '#submit' => array(
          'commerce_addressbook_pane_cancel',
        ),
        '#weight' => 100,
      );
    }
  }
  return $pane_form;
}
function _commerce_addressbook_add_profile_copy_checkbox(&$pane_form, $checkout_pane, $order, $target_type) {
  if (variable_get('commerce_' . $checkout_pane['pane_id'] . '_profile_copy', FALSE) && ($source_profile_type_name = variable_get('commerce_' . $checkout_pane['pane_id'] . '_profile_copy_source', NULL))) {

    // Load the default profile copy option from settings.
    $profile_copy_default = variable_get('commerce_' . $checkout_pane['pane_id'] . '_profile_copy_default', FALSE);

    // Make sure our profile type still exists..
    if ($source_profile_type = commerce_customer_profile_type_load($source_profile_type_name)) {
      $target_profile_type = commerce_customer_profile_type_load($target_type);
      $pane_form['commerce_customer_profile_copy'] = array(
        '#type' => 'checkbox',
        '#title' => t('My %target is the same as my %source.', array(
          '%target' => $target_profile_type['name'],
          '%source' => $source_profile_type['name'],
        )),
        '#element_validate' => array(
          'commerce_addressbook_profile_copy_validate',
        ),
        '#default_value' => isset($order->data['profile_copy'][$checkout_pane['pane_id']]['status']) ? $order->data['profile_copy'][$checkout_pane['pane_id']]['status'] : $profile_copy_default,
        '#weight' => -30,
        '#ajax' => array(
          'callback' => 'commerce_addressbook_pane_refresh',
          'wrapper' => strtr($checkout_pane['pane_id'], '_', '-') . '-ajax-wrapper',
        ),
        '#attached' => array(
          'css' => array(
            drupal_get_path('module', 'commerce_customer') . '/theme/commerce_customer.theme.css',
          ),
        ),
        '#prefix' => '<div class="commerce-customer-profile-copy">',
        '#suffix' => '</div>',
      );
    }
  }
}

/**
 * Element validate callback: processes input of the profile selection list.
 */
function commerce_addressbook_saved_addresses_validate($element, &$form_state, $form) {
  if (in_array('profile_selection', $form_state['triggering_element']['#parents']) && $form_state['triggering_element']['#id'] == $element['#id']) {
    $pane_id = $element['#parents'][0];

    // If the user wants to enter a new address.
    if ($element['#value'] == 'new_address') {
      global $user;
      $form_state['pane_' . $pane_id]['mode'] = 'edit';
      $type = substr($pane_id, 17);
      $form_state['pane_' . $pane_id]['profile'] = commerce_customer_profile_new($type, $user->uid);
    }
    else {
      $form_state['pane_' . $pane_id]['mode'] = 'view';
      $form_state['pane_' . $pane_id]['profile'] = commerce_customer_profile_load($element['#value']);
    }
    $form_state['rebuild'] = TRUE;
  }
}

/**
 * Element validate callback for the profile copy checkbox.
 */
function commerce_addressbook_profile_copy_validate($element, &$form_state, $form) {
  $triggering_element = end($form_state['triggering_element']['#array_parents']);
  $pane_id = reset($element['#array_parents']);
  $triggered_by_copy = $triggering_element == 'commerce_customer_profile_copy' && $form_state['triggering_element']['#id'] == $element['#id'];
  if ($triggering_element == 'commerce_customer_profile_copy') {
    $form_state['rebuild'] = TRUE;
  }

  // Checkbox: Off - Only invoked for the corresponding trigger element.
  if ($triggered_by_copy && empty($element['#value'])) {
    $form_state['order']->data['profile_copy'][$pane_id]['status'] = FALSE;

    // Make sure we return to the "view" mode if possible.
    $form_state['pane_' . $pane_id] = array();
    unset($form_state['order']->data['profile_copy'][$pane_id]['elements']);
    commerce_order_save($form_state['order']);
  }
  elseif (($triggered_by_copy || isset($form_state['triggering_element']['#button_type'])) && !empty($element['#value'])) {
    $type = substr($pane_id, 17);

    // Removes 'customer_profile_'
    $source_id = 'customer_profile_' . variable_get('commerce_' . $pane_id . '_profile_copy_source', '');
    $info = array(
      'commerce_customer_profile',
      $type,
      $pane_id,
    );
    if (isset($form_state['values'][$source_id]) && !isset($form_state['values'][$source_id]['profile_selection'])) {
      commerce_customer_profile_copy_fields($info, $form_state['input'][$pane_id], $form_state['input'][$source_id], $form_state);
      commerce_customer_profile_copy_fields($info, $form_state['values'][$pane_id], $form_state['values'][$source_id], $form_state);
    }
    else {

      // Use the profile from the source pane, if the source pane is present
      // on the page.
      if (isset($form_state['pane_' . $source_id])) {
        $profile = $form_state['pane_' . $source_id]['profile'];
      }
      else {

        // Retrieve the source profile from the order object if not empty.
        $field_name = variable_get('commerce_' . $source_id . '_field', '');
        if (!empty($form_state['order']->{$field_name})) {
          $order_wrapper = entity_metadata_wrapper('commerce_order', $form_state['order']);
          $profile = $order_wrapper->{$field_name}
            ->value();
        }
      }
      if (!empty($profile)) {
        commerce_customer_profile_copy_fields($info, $form_state['input'][$pane_id], $profile, $form_state);
        commerce_customer_profile_copy_fields($info, $form_state['values'][$pane_id], $profile, $form_state);
      }
    }
    $form_state['order']->data['profile_copy'][$pane_id]['status'] = TRUE;
    commerce_order_save($form_state['order']);

    // Unset any cached addressfield data for this customer profile.
    if (!empty($form_state['addressfield'])) {
      foreach ($form_state['addressfield'] as $key => $value) {
        if (strpos($key, 'commerce_customer_profile|' . $type) === 0) {
          unset($form_state['addressfield'][$key]);
        }
      }
    }
  }
}

/**
 * Ajax callback for the "edit" button.
 */
function commerce_addressbook_pane_refresh($form, &$form_state) {
  $pane_id = reset($form_state['triggering_element']['#parents']);
  return $form[$pane_id];
}

/**
 * Submit callback for the "edit" button.
 */
function commerce_addressbook_pane_edit($form, &$form_state) {
  $pane_id = reset($form_state['triggering_element']['#parents']);
  $form_state['pane_' . $pane_id]['mode'] = 'edit';
  $form_state['rebuild'] = TRUE;
}

/**
 * Submit callback for the "cancel" button.
 */
function commerce_addressbook_pane_cancel($form, &$form_state) {
  $pane_id = reset($form_state['triggering_element']['#parents']);
  $form_state['pane_' . $pane_id] = array();
  $form_state['rebuild'] = TRUE;
}

/**
 * Checkout pane callback: validates a customer profile edit form.
 */
function commerce_addressbook_pane_checkout_form_validate($form, &$form_state, $checkout_pane, $order) {
  $pane_id = $checkout_pane['pane_id'];
  $field_name = variable_get('commerce_' . $pane_id . '_field', '');

  // The specified profile reference field doesn't exist on this order type.
  // A consequence of the checkout settings applying for all order types.
  if ($field_name && !field_info_instance('commerce_order', $field_name, $order->type)) {
    return TRUE;
  }

  // Skip validation if the profile can't be found.
  if (!isset($form_state['pane_' . $pane_id]['profile']) || !isset($form_state['pane_' . $pane_id]['mode'])) {
    return TRUE;
  }
  $mode = $form_state['pane_' . $pane_id]['mode'];
  $source_profile_type = variable_get('commerce_' . $checkout_pane['pane_id'] . '_profile_copy_source', NULL);

  // Build the source profile pane identifier in order to check for
  // possible validation errors.
  if (!empty($source_profile_type)) {
    $source_id = 'customer_profile_' . $source_profile_type;
  }

  // Validate "edit" and "new" modes.
  if ($mode != 'view') {
    $profile = $form_state['pane_' . $pane_id]['profile'];

    // Notify field widgets to validate their data.
    field_attach_form_validate('commerce_customer_profile', $profile, $form[$checkout_pane['pane_id']], $form_state);

    // If any errors occurred in the field attach validate process for fields on
    // this checkout pane's customer profile, fail the checkout pane validation.
    if ($errors = form_get_errors()) {
      foreach ($errors as $field => $error) {
        if (substr($field, 0, strlen($pane_id) + 2) == $pane_id . '][') {
          return FALSE;
        }
        elseif ($mode == 'copy' && !empty($source_id)) {
          if (substr($field, 0, strlen($source_id) + 2) == $source_id . '][') {
            return FALSE;
          }
        }
      }
    }
  }
  return TRUE;
}

/**
 * Checkout pane callback: submits a customer profile edit form.
 */
function commerce_addressbook_pane_checkout_form_submit($form, &$form_state, $checkout_pane, $order) {
  $pane_id = $checkout_pane['pane_id'];
  $field_name = variable_get('commerce_' . $pane_id . '_field', '');

  // The specified profile reference field doesn't exist on this order type.
  // A consequence of the checkout settings applying for all order types.
  if ($field_name && !field_info_instance('commerce_order', $field_name, $order->type)) {
    return;
  }

  // Skip validation if the profile can't be found.
  if (!isset($form_state['pane_' . $pane_id]['profile']) || !isset($form_state['pane_' . $pane_id]['mode'])) {
    return;
  }
  $profile = $form_state['pane_' . $pane_id]['profile'];

  // Save the customer profile in the "new" and "edit" modes.
  if ($form_state['pane_' . $pane_id]['mode'] != 'view') {

    // Ensure the profile is active.
    $profile->status = TRUE;

    // Set the profile's uid if it's being created at this time.
    if (empty($profile->profile_id)) {
      $profile->uid = $order->uid;
    }

    // Notify field widgets.
    field_attach_submit('commerce_customer_profile', $profile, $form[$checkout_pane['pane_id']], $form_state);

    // Make sure that the incomplete flag is removed, if it was previously set.
    if (!empty($profile->data['incomplete'])) {
      unset($profile->data['incomplete']);
    }

    // Save the profile.
    commerce_customer_profile_save($profile);
  }

  // Store the profile ID for the related field as specified on the settings form.
  $wrapper = entity_metadata_wrapper('commerce_order', $order);
  if ($field_name) {
    $wrapper->{$field_name} = $profile;
  }
  else {

    // Or make the association in the order's data array if no field was found.
    $order->data['profiles'][$checkout_pane['pane_id']] = $profile->profile_id;
  }
}

/**
 * Checkout pane callback: returns the cart contents review data for the
 *   Review checkout pane.
 *
 * Copied verbatim from commerce_customer_profile_pane_review.
 */
function commerce_addressbook_pane_review($form, $form_state, $checkout_pane, $order) {

  // Load the profile based on the related customer profile reference field...
  if ($field_name = variable_get('commerce_' . $checkout_pane['pane_id'] . '_field', '')) {
    $profile = entity_metadata_wrapper('commerce_order', $order)->{$field_name}
      ->value();
  }
  else {

    // Or use the association stored in the order's data array if no field is set.
    $profile = commerce_customer_profile_load($order->data['profiles'][$checkout_pane['pane_id']]);
  }
  if ($profile) {
    $content = entity_view('commerce_customer_profile', array(
      $profile->profile_id => $profile,
    ), 'customer');
    return drupal_render($content);
  }
  else {
    return t('No information');
  }
}

Functions

Namesort descending Description
commerce_addressbook_pane_cancel Submit callback for the "cancel" button.
commerce_addressbook_pane_checkout_form Checkout pane callback: returns a customer profile edit form.
commerce_addressbook_pane_checkout_form_submit Checkout pane callback: submits a customer profile edit form.
commerce_addressbook_pane_checkout_form_validate Checkout pane callback: validates a customer profile edit form.
commerce_addressbook_pane_edit Submit callback for the "edit" button.
commerce_addressbook_pane_refresh Ajax callback for the "edit" button.
commerce_addressbook_pane_review Checkout pane callback: returns the cart contents review data for the Review checkout pane.
commerce_addressbook_pane_settings_form Checkout pane callback: returns the customer profile pane's settings form.
commerce_addressbook_profile_copy_validate Element validate callback for the profile copy checkbox.
commerce_addressbook_saved_addresses_validate Element validate callback: processes input of the profile selection list.
_commerce_addressbook_add_profile_copy_checkbox