You are here

commerce_addressbook.module in Commerce Addressbook 7.2

Same filename and directory in other branches
  1. 7.3 commerce_addressbook.module
  2. 7 commerce_addressbook.module

Defines addressbook functionality for customer profiles, allowing them to be reused and managed on a per-user basis.

File

commerce_addressbook.module
View source
<?php

/**
 * @file
 * Defines addressbook functionality for customer profiles, allowing them to be
 * reused and managed on a per-user basis.
 */

/**
 * Implements hook_admin_paths().
 */
function commerce_addressbook_admin_paths() {
  return array(
    'user/*/addressbook/*/create' => TRUE,
    'user/*/addressbook/*/edit/*' => TRUE,
    'user/*/addressbook/*/delete/*' => TRUE,
  );
}

/**
 * Implements hook_hook_info().
 */
function commerce_addressbook_hook_info() {
  $hooks = array(
    'commerce_addressbook_labels_alter' => array(
      'group' => 'commerce',
    ),
  );
  return $hooks;
}

/**
 * Implements hook_menu().
 */
function commerce_addressbook_menu() {
  $items = array();
  $items['user/%user/addressbook'] = array(
    'title' => 'Address Book',
    'page callback' => 'commerce_addressbook_page',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'commerce_addressbook_page_access',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'includes/commerce_addressbook.user.inc',
    'weight' => 20,
  );

  // Custom administrative components for managing customer profile entities
  // from the user pages.
  foreach (commerce_customer_profile_types() as $type => $profile_type) {
    $items['user/%user/addressbook/' . $type] = array(
      'page callback' => 'commerce_addressbook_profile_page',
      'page arguments' => array(
        1,
        $type,
      ),
      'access callback' => 'commerce_addressbook_profile_page_access',
      'access arguments' => array(
        1,
        $type,
      ),
      'title' => $profile_type['name'],
      'type' => MENU_LOCAL_TASK,
      'file' => 'includes/commerce_addressbook.user.inc',
    );
    $items['user/%user/addressbook/' . $type . '/create'] = array(
      'page callback' => 'commerce_addressbook_profile_create',
      'page arguments' => array(
        1,
        $type,
      ),
      'access callback' => 'commerce_addressbook_profile_create_access',
      'access arguments' => array(
        1,
        $type,
      ),
      'title' => 'Add an address',
      'type' => MENU_LOCAL_ACTION,
      'file' => 'includes/commerce_addressbook.user.inc',
    );
    $items['user/%user/addressbook/' . $type . '/edit/%commerce_customer_profile'] = array(
      'page callback' => 'commerce_addressbook_profile_options_edit',
      'page arguments' => array(
        1,
        5,
      ),
      'access callback' => 'commerce_addressbook_profile_access',
      'access arguments' => array(
        'update',
        5,
      ),
      'type' => MENU_CALLBACK,
      'file' => 'includes/commerce_addressbook.user.inc',
    );
    $items['user/%user/addressbook/' . $type . '/default/%commerce_customer_profile'] = array(
      'page callback' => 'commerce_addressbook_profile_options_default',
      'page arguments' => array(
        1,
        5,
      ),
      'access callback' => 'commerce_addressbook_profile_access',
      'access arguments' => array(
        'update',
        5,
      ),
      'type' => MENU_CALLBACK,
      'file' => 'includes/commerce_addressbook.user.inc',
    );
    $items['user/%user/addressbook/' . $type . '/delete/%commerce_customer_profile'] = array(
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'commerce_addressbook_profile_options_delete_form',
        1,
        5,
      ),
      'access callback' => 'commerce_addressbook_profile_access',
      'access arguments' => array(
        'delete',
        5,
      ),
      'type' => MENU_CALLBACK,
      'file' => 'includes/commerce_addressbook.user.inc',
    );
  }
  return $items;
}

/**
 * Access callback for path /user/%user/addressbook.
 *
 * Return the first enabled profile type if there's one, or FALSE.
 */
function commerce_addressbook_page_access($account) {
  foreach (commerce_customer_profile_types() as $type => $profile_type) {
    if (commerce_addressbook_profile_page_access($account, $type)) {
      return $type;
    }
  }
  return FALSE;
}

/**
 * Access callback: determine if the user can create a customer profile of the
 * given type.
 */
function commerce_addressbook_profile_create_access($account, $type) {
  global $user;

  // The addressbook is not enabled for this profile type.
  if (!variable_get('commerce_customer_profile_' . $type . '_addressbook', FALSE)) {
    return FALSE;
  }

  // The user has admin privileges, or is on his own pages.
  if (user_access('administer commerce_customer_profile entities') || $user->uid == $account->uid) {
    if (user_access('create commerce_customer_profile entities') || user_access('create commerce_customer_profile entities of bundle ' . $type)) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Access callback for entity-level operations. Acts as a wrapper around
 * commerce_customer_profile_access.
 */
function commerce_addressbook_profile_access($op, $customer_profile) {

  // The addressbook is not enabled for this profile type.
  if (!variable_get('commerce_customer_profile_' . $customer_profile->type . '_addressbook', FALSE)) {
    return FALSE;
  }
  return commerce_customer_profile_access($op, $customer_profile);
}

/**
 * Access callback: determine if the user can access the listing page of a
 * given profile type.
 */
function commerce_addressbook_profile_page_access($account, $profile_type) {
  global $user;

  // The addressbook is not enabled for this profile type.
  if (!variable_get('commerce_customer_profile_' . $profile_type . '_addressbook', FALSE)) {
    return FALSE;
  }

  // Check if the user can access any page.
  if (user_access('administer commerce_customer_profile entities') || user_access('view any commerce_customer_profile entity') || user_access('view any commerce_customer_profile entity of bundle ' . $profile_type) || user_access('edit any commerce_customer_profile entity') || user_access('edit any commerce_customer_profile entity of bundle ' . $profile_type)) {
    return TRUE;
  }

  // Check if the user can access his own page.
  if ($user->uid == $account->uid) {
    if (user_access('view own commerce_customer_profile entities') || user_access('create commerce_customer_profile entities') || user_access('create commerce_customer_profile entities of bundle ' . $profile_type) || user_access('view own commerce_customer_profile entities of bundle ' . $profile_type) || user_access('edit own commerce_customer_profile entities') || user_access('edit own commerce_customer_profile entities of bundle ' . $profile_type)) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Implements hook_entity_info_alter().
 */
function commerce_addressbook_entity_info_alter(&$info) {
  $info['commerce_customer_profile']['view modes']['addressbook'] = array(
    'label' => t('Addressbook'),
    'custom settings' => FALSE,
  );
}

/**
 * Implements hook_entity_view().
 *
 * Adds the "edit", "delete" and "set as default" links to the customer profile.
 */
function commerce_addressbook_entity_view($entity, $type, $view_mode, $langcode) {
  if ($type == 'commerce_customer_profile' && $view_mode == 'addressbook') {
    $links = array();
    global $user;
    if (commerce_addressbook_profile_access('update', $entity)) {
      static $record;
      if (empty($record) || $record->uid != $user->uid || $record->type != $entity->type) {
        $query = db_select('commerce_addressbook_defaults', 'cad')
          ->fields('cad')
          ->condition('uid', $entity->uid)
          ->condition('type', $entity->type)
          ->execute();
        $record = $query
          ->fetchObject();
      }
      drupal_add_library('system', 'drupal.ajax');
      if (empty($record) || $record->profile_id != $entity->profile_id) {
        $links['default'] = array(
          '#theme' => 'link',
          '#text' => t('set as default'),
          '#path' => 'user/' . $entity->uid . '/addressbook/' . $entity->type . '/default/' . $entity->profile_id . '/nojs',
          '#options' => array(
            'attributes' => array(
              'class' => array(
                'use-ajax',
              ),
            ),
            'html' => FALSE,
          ),
          '#suffix' => ' | ',
        );
      }
      $links['edit'] = array(
        '#theme' => 'link',
        '#text' => t('edit'),
        '#path' => 'user/' . $entity->uid . '/addressbook/' . $entity->type . '/edit/' . $entity->profile_id,
        '#options' => array(
          'attributes' => array(),
          'html' => FALSE,
        ),
        '#suffix' => ' | ',
      );
    }
    if (commerce_addressbook_profile_access('delete', $entity)) {
      $links['delete'] = array(
        '#theme' => 'link',
        '#text' => t('delete'),
        '#path' => 'user/' . $entity->uid . '/addressbook/' . $entity->type . '/delete/' . $entity->profile_id,
        '#options' => array(
          'attributes' => array(),
          'html' => FALSE,
        ),
      );
    }
    if ($links) {
      $entity->content['commerce_addressbook_options'] = array_merge(array(
        '#weight' => 100,
        '#prefix' => '<div class="addressbook-links">',
        '#suffix' => '</div>',
      ), $links);
    }
  }
}

/**
 * Implements hook_commerce_customer_profile_delete().
 *
 * Delete the customer profile entry from commerce_addressbook_defaults table,
 * set a new default customer profile for this type.
 */
function commerce_addressbook_commerce_customer_profile_delete($profile) {
  $default_profile = commerce_addressbook_get_default_profile_id($profile->uid, $profile->type);
  if ($default_profile == $profile->profile_id) {
    db_delete('commerce_addressbook_defaults')
      ->condition('profile_id', $profile->profile_id)
      ->execute();
    $query = new EntityFieldQuery();
    $query
      ->entityCondition('entity_type', 'commerce_customer_profile')
      ->propertyCondition('status', 1)
      ->propertyCondition('type', $profile->type)
      ->propertyOrderBy('profile_id', 'DESC')
      ->range(0, 1);
    $results = $query
      ->execute();
    if (!empty($results['commerce_customer_profile'])) {
      commerce_addressbook_set_default_profile(commerce_customer_profile_load(key($results['commerce_customer_profile'])));
    }
  }
}

/**
 * Implements hook_forms().
 */
function commerce_addressbook_forms() {
  $forms = array();

  // Define a wrapper ID for the customer profile form used by addressbook.
  $forms['commerce_addressbook_customer_profile_form'] = array(
    'callback' => 'commerce_customer_customer_profile_form',
  );
  return $forms;
}

/**
 * Implements hook_form_alter().
 */
function commerce_addressbook_form_alter(&$form, &$form_state, $form_id) {
  global $user;

  // If we're dealing with a commerce checkout form.
  if (strpos($form_id, 'commerce_checkout_form_') === 0 && $user->uid > 0) {
    $checkout_page_id = substr($form_id, 23);

    // Find all panes for our current checkout page.
    foreach (commerce_checkout_panes(array(
      'enabled' => TRUE,
      'page' => $checkout_page_id,
    )) as $pane_id => $checkout_pane) {

      // If this pane is a customer profile based pane build a list of previous
      // profiles from which to pick that are of the same bundle.
      if (substr($pane_id, 0, 16) == 'customer_profile' && isset($form[$pane_id]) && variable_get('commerce_' . $pane_id . '_addressbook', FALSE)) {
        $field = field_info_field(variable_get('commerce_' . $pane_id . '_field', ''));

        // Load the customer profiles via an EntityFieldQuery(), tag it in order
        // to allow query alterations.
        $query = new EntityFieldQuery();
        $query
          ->entityCondition('entity_type', 'commerce_customer_profile')
          ->entityCondition('bundle', $field['settings']['profile_type'])
          ->propertyCondition('uid', $user->uid)
          ->propertyCondition('status', TRUE)
          ->addTag('commerce_addressbook');
        $results = $query
          ->execute();
        if (isset($results['commerce_customer_profile'])) {
          $profiles = commerce_customer_profile_load_multiple(array_keys($results['commerce_customer_profile']));

          // Prepare the options.
          $options = array();
          foreach ($profiles as $id => $profile) {
            $field_values = field_get_items('commerce_customer_profile', $profile, 'commerce_customer_address');
            $options[$id] = $field_values[0]['thoroughfare'];
          }
          drupal_alter('commerce_addressbook_labels', $options, $profiles);

          // Prepare the default value.
          $reference_field_name = variable_get('commerce_' . $pane_id . '_field', '');
          $order_wrapper = entity_metadata_wrapper('commerce_order', $form_state['order']);
          $profile_reference = $order_wrapper->{$reference_field_name}
            ->value();
          $default_value = 'none';
          if (!empty($form_state['values'][$pane_id]['addressbook'])) {
            $default_value = $form_state['values'][$pane_id]['addressbook'];
          }
          elseif (!empty($profile_reference->profile_id)) {
            $default_value = $profile_reference->profile_id;
          }

          // Check if the default profile is in the enabled profiles array.
          if (!isset($profiles[$default_value])) {
            $default_value = 'none';
          }
          if ($default_value != 'none') {

            // Make sure our profile type still exists..
            if (!empty($form[$pane_id]['commerce_customer_profile_copy'])) {
              if (($source_profile_type_name = variable_get('commerce_' . $pane_id . '_profile_copy_source', NULL)) && ($source_profile_type = commerce_customer_profile_type_load($source_profile_type_name))) {

                // Disable the profile copy checkbox.
                $form[$pane_id]['commerce_customer_profile_copy']['#default_value'] = FALSE;
                $form[$pane_id]['commerce_customer_profile_copy']['#access'] = FALSE;

                // Loop over source profile fields and enable previously disabled
                // fields.
                foreach (field_info_instances('commerce_customer_profile', $source_profile_type['type']) as $field_name => $field) {
                  if (!empty($form[$pane_id][$field_name])) {
                    $langcode = $form[$pane_id][$field_name]['#language'];
                    $form[$pane_id][$field_name][$langcode]['#access'] = TRUE;
                    foreach (element_children($form[$pane_id][$field_name][$langcode]) as $key) {
                      $form[$pane_id][$field_name][$langcode][$key]['#access'] = TRUE;

                      // Disable the editing of the profile if the user doesn't
                      // have the right to edit customer profiles.
                      if (!commerce_customer_profile_access('update', $profiles[$default_value])) {
                        $form[$pane_id][$field_name][$langcode][$key]['#disabled'] = TRUE;
                      }
                    }
                  }
                }
              }
            }
          }
          $form[$pane_id]['#prefix'] = '<div id="' . strtr($pane_id, '_', '-') . '-ajax-wrapper">';
          $form[$pane_id]['#suffix'] = '</div>';
          $form[$pane_id]['addressbook_entries'] = array(
            '#type' => 'value',
            '#value' => $profiles,
          );
          $form[$pane_id]['addressbook'] = array(
            '#addressbook' => TRUE,
            '#type' => 'select',
            '#title' => t('Addresses on File'),
            '#description' => t('You may select a pre-existing address on file.'),
            '#options' => $options,
            '#empty_option' => t('-- Choose --'),
            '#empty_value' => 'none',
            '#ajax' => array(
              'callback' => 'commerce_addressbook_checkout_form_callback',
              'wrapper' => strtr($pane_id, '_', '-') . '-ajax-wrapper',
            ),
            '#element_validate' => array(
              'commerce_addressbook_saved_addresses_validate',
            ),
            '#weight' => -100,
            '#default_value' => $default_value,
          );
        }
      }
    }
  }
  if ($form_id == 'commerce_checkout_pane_settings_form' && substr($form['checkout_pane']['#value']['pane_id'], 0, 16) == 'customer_profile') {
    $form['settings']['commerce_' . $form['checkout_pane']['#value']['pane_id'] . '_addressbook'] = array(
      '#type' => 'checkbox',
      '#title' => t('Enable the Address Book'),
      '#description' => t('This will allow authenticated users to reuse previously entered addresses.'),
      '#default_value' => variable_get('commerce_' . $form['checkout_pane']['#value']['pane_id'] . '_addressbook', FALSE),
    );
  }
  if ($form_id == 'commerce_addressbook_customer_profile_form') {

    // Make sure the submit handlers run.
    rsort($form['actions']['submit']['#submit']);
    foreach ($form['actions']['submit']['#submit'] as $callback) {
      array_unshift($form['#submit'], $callback);
    }
    unset($form['actions']['submit']['#submit']);

    // Hide the "Additional settings" vertical tabs.
    $form['additional_settings']['#access'] = FALSE;
  }
}

/**
 * Ajax callback for replacing the appropriate commerce customer checkout pane.
 */
function commerce_addressbook_checkout_form_callback($form, $form_state) {
  $pane_id = $form_state['triggering_element']['#parents'][0];

  // Add replace element as default AJAX command.
  $commands = array();
  $commands[] = ajax_command_replace(NULL, drupal_render($form[$pane_id]));

  // Allow other modules to add arbitrary AJAX commands on the refresh.
  drupal_alter('commerce_addressbook_callback', $commands, $form, $form_state);
  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}

/**
 * Element validate callback: processes input of the address select list.
 */
function commerce_addressbook_saved_addresses_validate($element, &$form_state, $form) {
  if (in_array('addressbook', $form_state['triggering_element']['#parents']) && $form_state['triggering_element']['#id'] == $element['#id']) {
    $pane_id = $element['#parents'][0];
    $field_name = variable_get('commerce_' . $pane_id . '_field', '');

    // Load the latest version of the order.
    $order = commerce_order_load($form_state['order']->order_id);
    $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
    if (is_numeric($element['#value'])) {
      global $user;
      $profile = commerce_customer_profile_load($element['#value']);

      // Validate that the user has access to the selected profile.
      if (!commerce_customer_profile_access('view', $profile, $user)) {
        drupal_set_message(t('You must have access to the selected profile.'), 'error');
        return;
      }

      // If we detect a change in the element's value, and the customer profile
      // reference isn't already set to the specified value...
      if ($order_wrapper->{$field_name}
        ->raw() != $element['#value']) {

        // Update the order based on the value and rebuild the form.
        if ($element['#value'] == 0) {
          $order_wrapper->{$field_name} = NULL;
        }
        else {
          $order_wrapper->{$field_name} = $element['#value'];
        }
      }
    }
    else {
      $order_wrapper->{$field_name} = NULL;
    }

    // Save the changed customer profile to the order.
    $order_wrapper
      ->save();
    unset($form_state['input'][$pane_id]);
    $element_key = isset($form[$pane_id]['commerce_customer_address'][$form[$pane_id]['commerce_customer_address']['#language']][0]['element_key']['#value']) ? $form[$pane_id]['commerce_customer_address'][$form[$pane_id]['commerce_customer_address']['#language']][0]['element_key']['#value'] : '';
    if (!empty($element_key)) {
      unset($form_state['addressfield'][$element_key]);
    }
  }
}

/**
 * Find an existing default for a given user, for a given profile type and
 * update it to a new profile, or set it if a default is not already set.
 */
function commerce_addressbook_set_default_profile($customer_profile) {
  db_merge('commerce_addressbook_defaults')
    ->key(array(
    'uid' => $customer_profile->uid,
    'type' => $customer_profile->type,
  ))
    ->fields(array(
    'profile_id' => $customer_profile->profile_id,
  ))
    ->execute();

  // Allow modules to react to change in customer profile default change.
  module_invoke_all('commerce_addressbook_set_default', $customer_profile);
  rules_invoke_event('commerce_addressbook_set_default', $customer_profile);
}

/**
 * Returns the default profile id for a specific uid and profile type.
 *
 * @param $uid
 *   The uid of the user whose default profile id should be returned.
 * @param $type
 *   The type of customer profile to look up.
 *
 * @return
 *  The id of the default profile if set, FALSE otherwise.
 */
function commerce_addressbook_get_default_profile_id($uid, $type) {
  $query = db_select('commerce_addressbook_defaults', 'cad')
    ->fields('cad', array(
    'profile_id',
  ))
    ->condition('uid', $uid)
    ->condition('type', $type)
    ->execute();
  $record = $query
    ->fetchObject();
  return $record ? $record->profile_id : FALSE;
}

/**
 * Implements hook_commerce_order_presave().
 *
 * This hook sets the default profile as initial value on the corresponding
 * checkout panes.
 */
function commerce_addressbook_commerce_order_presave($order) {
  if ($order->uid) {
    foreach (commerce_checkout_panes() as $pane_id => $checkout_pane) {

      // Only for panes for which the address book is enabled.
      if (variable_get('commerce_' . $pane_id . '_addressbook', FALSE)) {
        $type = substr($checkout_pane['pane_id'], 17);

        // Removes 'customer_profile_'
        // Does this profile type have a default?
        $default_profile_id = commerce_addressbook_get_default_profile_id($order->uid, $type);
        if ($default_profile_id) {

          // Is this profile stored in a field or in the data association?
          if (($field_name = variable_get('commerce_' . $pane_id . '_field', '')) && empty($order->{$field_name})) {

            // Check to ensure the specified profile reference field exists on
            // the current order type.
            if (field_info_instance('commerce_order', $field_name, $order->type)) {
              $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
              $order_wrapper->{$field_name} = $default_profile_id;
            }
          }
          else {

            // Or store it in the association stored in the order's data array
            // if no field is set.
            $order->data['profiles'][$checkout_pane['pane_id']] = $default_profile_id;
          }
        }
      }
    }
  }
}

/**
 * Implements hook_commerce_customer_profile_insert().
 *
 * Set the new customer profile as default if there's none.
 */
function commerce_addressbook_commerce_customer_profile_insert($profile) {
  if (!empty($profile->profile_id) && $profile->uid != 0) {
    $default_profile = commerce_addressbook_get_default_profile_id($profile->uid, $profile->type);
    if (!$default_profile) {
      commerce_addressbook_set_default_profile($profile);
    }
  }
}

/**
 * Implements hook_commerce_customer_profile_update().
 *
 * Set the last updated customer profile as default if there's none.
 */
function commerce_addressbook_commerce_customer_profile_update($profile) {
  if (!empty($profile->profile_id) && $profile->uid != 0) {
    $default_profile = commerce_addressbook_get_default_profile_id($profile->uid, $profile->type);
    if (!$default_profile) {
      commerce_addressbook_set_default_profile($profile);
    }
  }
}

/**
 * Implements hook_views_api().
 */
function commerce_addressbook_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'commerce_addressbook') . '/includes/views',
  );
}

/**
 * Retrieve a View.
 *
 * @param $view_key
 *   The ID of the View to embed.
 * @param $display_id
 *   The ID of the display of the View.
 * @param $arguments
 *   An array of arguments to pass to the View.
 * @param $override_url
 *   A url that overrides the url of the current view.
 *
 * @return
 *   The views object, you'll have to call render if to render it.
 */
function commerce_addressbook_retrieve_view($view_id, $display_id, $arguments, $override_url = '') {

  // Load the specified View.
  $view = views_get_view($view_id);
  $view
    ->set_display($display_id);

  // Set the specific arguments passed in.
  $view
    ->set_arguments($arguments);

  // Override the view url, if an override was provided.
  if (!empty($override_url)) {
    $view->override_url = $override_url;
  }

  // Prepare and execute the View query.
  $view
    ->pre_execute();
  $view
    ->execute();

  // Return the view.
  return $view;
}

Functions

Namesort descending Description
commerce_addressbook_admin_paths Implements hook_admin_paths().
commerce_addressbook_checkout_form_callback Ajax callback for replacing the appropriate commerce customer checkout pane.
commerce_addressbook_commerce_customer_profile_delete Implements hook_commerce_customer_profile_delete().
commerce_addressbook_commerce_customer_profile_insert Implements hook_commerce_customer_profile_insert().
commerce_addressbook_commerce_customer_profile_update Implements hook_commerce_customer_profile_update().
commerce_addressbook_commerce_order_presave Implements hook_commerce_order_presave().
commerce_addressbook_entity_info_alter Implements hook_entity_info_alter().
commerce_addressbook_entity_view Implements hook_entity_view().
commerce_addressbook_forms Implements hook_forms().
commerce_addressbook_form_alter Implements hook_form_alter().
commerce_addressbook_get_default_profile_id Returns the default profile id for a specific uid and profile type.
commerce_addressbook_hook_info Implements hook_hook_info().
commerce_addressbook_menu Implements hook_menu().
commerce_addressbook_page_access Access callback for path /user/%user/addressbook.
commerce_addressbook_profile_access Access callback for entity-level operations. Acts as a wrapper around commerce_customer_profile_access.
commerce_addressbook_profile_create_access Access callback: determine if the user can create a customer profile of the given type.
commerce_addressbook_profile_page_access Access callback: determine if the user can access the listing page of a given profile type.
commerce_addressbook_retrieve_view Retrieve a View.
commerce_addressbook_saved_addresses_validate Element validate callback: processes input of the address select list.
commerce_addressbook_set_default_profile Find an existing default for a given user, for a given profile type and update it to a new profile, or set it if a default is not already set.
commerce_addressbook_views_api Implements hook_views_api().