You are here

commerce_customer.module in Commerce Core 7

Defines the customer profile entity and API functions to manage customers and interact with them.

File

modules/customer/commerce_customer.module
View source
<?php

/**
 * @file
 * Defines the customer profile entity and API functions to manage customers and
 * interact with them.
 */

/**
 * Implements hook_entity_info().
 */
function commerce_customer_entity_info() {
  $return = array(
    'commerce_customer_profile' => array(
      'label' => t('Commerce Customer profile'),
      'controller class' => 'CommerceCustomerProfileEntityController',
      'base table' => 'commerce_customer_profile',
      'revision table' => 'commerce_customer_profile_revision',
      'fieldable' => TRUE,
      'entity keys' => array(
        'id' => 'profile_id',
        'revision' => 'revision_id',
        'bundle' => 'type',
      ),
      'bundle keys' => array(
        'bundle' => 'type',
      ),
      'bundles' => array(),
      'load hook' => 'commerce_customer_profile_load',
      'view modes' => array(
        // Neither of these provide a full view of the profile but rather give
        // the summary of field data as seen on the checkout form or in the
        // customer profile reference field's display formatter.
        'administrator' => array(
          'label' => t('Administrator'),
          'custom settings' => FALSE,
        ),
        'customer' => array(
          'label' => t('Customer'),
          'custom settings' => FALSE,
        ),
      ),
      'uri callback' => 'commerce_customer_profile_uri',
      'label callback' => 'commerce_customer_profile_label',
      'token type' => 'commerce-customer-profile',
      'metadata controller class' => '',
      'access callback' => 'commerce_entity_access',
      'access arguments' => array(
        'user key' => 'uid',
        'access tag' => 'commerce_customer_profile_access',
      ),
      'permission labels' => array(
        'singular' => t('customer profile'),
        'plural' => t('customer profiles'),
      ),
      // Prevent Redirect alteration of the customer form.
      'redirect' => FALSE,
    ),
  );
  foreach (commerce_customer_profile_type_get_name() as $type => $name) {
    $return['commerce_customer_profile']['bundles'][$type] = array(
      'label' => $name,
    );
  }
  return $return;
}

/**
 * Entity uri callback: gives modules a chance to specify a path for a customer
 * profile.
 */
function commerce_customer_profile_uri($profile) {

  // Allow modules to specify a path, returning the first one found.
  foreach (module_implements('commerce_customer_profile_uri') as $module) {
    $uri = module_invoke($module, 'commerce_customer_profile_uri', $profile);

    // If the implementation returned data, use that now.
    if (!empty($uri)) {
      return $uri;
    }
  }
  return NULL;
}

/**
 * Entity label callback: returns the label for an individual customer profile.
 */
function commerce_customer_profile_label($profile) {

  // Load the customer profile type to look find the label callback.
  $profile_type = commerce_customer_profile_type_load($profile->type);

  // Make sure we get a valid label callback.
  $callback = $profile_type['label_callback'];
  if (!is_callable($callback)) {
    $callback = 'commerce_customer_profile_default_label';
  }
  return $callback($profile);
}

/**
 * Returns the default label for a customer profile.
 *
 * @param $profile
 *   A fully loaded customer profile object.
 *
 * @return
 *   The full name of the default address if available or the profile ID.
 */
function commerce_customer_profile_default_label($profile) {
  $label = '';

  // If the profile has a default address field...
  if (!empty($profile->commerce_customer_address)) {

    // Wrap the customer profile object for easier access to its field data.
    $profile_wrapper = entity_metadata_wrapper('commerce_customer_profile', $profile);
    if (isset($profile_wrapper->commerce_customer_address->name_line)) {
      $label = $profile_wrapper->commerce_customer_address->name_line
        ->value();
    }
  }

  // Return the profile ID if we couldn't derive a label from an address field.
  if (empty($label)) {
    $label = $profile->profile_id;
  }
  return $label;
}

/**
 * Implements hook_hook_info().
 */
function commerce_customer_hook_info() {
  $hooks = array(
    'commerce_customer_profile_type_info' => array(
      'group' => 'commerce',
    ),
    'commerce_customer_profile_type_info_alter' => array(
      'group' => 'commerce',
    ),
    'commerce_customer_profile_uri' => array(
      'group' => 'commerce',
    ),
    'commerce_customer_profile_view' => array(
      'group' => 'commerce',
    ),
    'commerce_customer_profile_presave' => array(
      'group' => 'commerce',
    ),
    'commerce_customer_profile_insert' => array(
      'group' => 'commerce',
    ),
    'commerce_customer_profile_update' => array(
      'group' => 'commerce',
    ),
    'commerce_customer_profile_delete' => array(
      'group' => 'commerce',
    ),
    'commerce_customer_profile_can_delete' => array(
      'group' => 'commerce',
    ),
  );
  return $hooks;
}

/**
 * Implements hook_enable().
 */
function commerce_customer_enable() {
  commerce_customer_configure_customer_types();
}

/**
 * Implements hook_modules_enabled().
 */
function commerce_customer_modules_enabled($modules) {
  commerce_customer_configure_customer_fields($modules);
}

/**
 * Configures customer profile types defined by enabled modules.
 */
function commerce_customer_configure_customer_types() {
  foreach (commerce_customer_profile_types() as $type => $profile_type) {
    commerce_customer_configure_customer_profile_type($profile_type);
  }
}

/**
 * Ensures the address field is present on the specified customer profile bundle.
 */
function commerce_customer_configure_customer_profile_type($profile_type) {
  if ($profile_type['addressfield']) {

    // Look for or add an address field to the customer profile type.
    $field_name = 'commerce_customer_address';
    commerce_activate_field($field_name);
    field_cache_clear();
    $field = field_info_field($field_name);
    $instance = field_info_instance('commerce_customer_profile', $field_name, $profile_type['type']);
    if (empty($field)) {
      $field = array(
        'field_name' => $field_name,
        'type' => 'addressfield',
        'cardinality' => 1,
        'entity_types' => array(
          'commerce_customer_profile',
        ),
        'translatable' => FALSE,
      );
      $field = field_create_field($field);
    }
    if (empty($instance)) {
      $instance = array(
        'field_name' => $field_name,
        'entity_type' => 'commerce_customer_profile',
        'bundle' => $profile_type['type'],
        'label' => t('Address'),
        'required' => TRUE,
        'widget' => array(
          'type' => 'addressfield_standard',
          'weight' => -10,
          'settings' => array(
            'format_handlers' => array(
              'address',
              'name-oneline',
            ),
          ),
        ),
        'display' => array(),
      );

      // Set the default display formatters for various view modes.
      foreach (array(
        'default',
        'customer',
        'administrator',
      ) as $view_mode) {
        $instance['display'][$view_mode] = array(
          'label' => 'hidden',
          'type' => 'addressfield_default',
          'weight' => -10,
        );
      }
      field_create_instance($instance);
    }
  }
}

/**
 * Configures fields referencing customer profile types defined by enabled
 * modules and configures the fields on those profile types if necessary.
 *
 * @param $modules
 *   An array of module names whose customer profile type fields should be
 *   configured; if left NULL, will default to all modules that implement
 *   hook_commerce_customer_profile_type_info().
 */
function commerce_customer_configure_customer_fields($modules = NULL) {

  // If no modules array is passed, recheck the fields for all customer profile
  // types defined by enabled modules.
  if (empty($modules)) {
    $modules = module_implements('commerce_customer_profile_type_info');
  }

  // Reset the customer profile type static cache to ensure we get types added
  // by newly enabled modules.
  commerce_customer_profile_types_reset();

  // Loop through all the enabled modules.
  foreach ($modules as $module) {

    // If the module implements hook_commerce_customer_profile_type_info()...
    if (module_hook($module, 'commerce_customer_profile_type_info')) {
      $profile_types = module_invoke($module, 'commerce_customer_profile_type_info');

      // If this profile type has been previously disabled, update any reference
      // fields to be active again before attempting to recreate them.
      $activated = FALSE;
      foreach ($profile_types as $type => $profile_type) {
        foreach (field_read_fields(array(
          'type' => 'commerce_customer_profile_reference',
          'active' => 0,
          'storage_active' => 1,
          'deleted' => 0,
        ), array(
          'include_inactive' => TRUE,
        )) as $field_name => $field) {

          // If this field references profiles of the re-enabled type...
          if ($field['settings']['profile_type'] == $type) {
            if (commerce_activate_field($field_name)) {
              $activated = TRUE;
            }
          }
        }
      }

      // Clear the field cache if any profile reference fields were activated.
      if ($activated) {
        field_cache_clear();
      }

      // Loop through and configure the customer profile types defined by the module.
      foreach ($profile_types as $type => $profile_type) {

        // Default the addressfield property if it isn't set.
        $profile_type = array_merge(array(
          'addressfield' => TRUE,
        ), $profile_type);
        commerce_customer_configure_customer_profile_type($profile_type);
      }
    }
  }
}

/**
 * Implements hook_modules_disabled().
 */
function commerce_customer_modules_disabled($modules) {

  // Loop through all the disabled modules.
  foreach ($modules as $module) {

    // If the module implements hook_commerce_customer_profile_type_info()...
    if (module_hook($module, 'commerce_customer_profile_type_info')) {
      $profile_types = module_invoke($module, 'commerce_customer_profile_type_info');
      if (!empty($profile_types)) {

        // Disable any profiles of the types disabled.
        $query = db_update('commerce_customer_profile')
          ->fields(array(
          'status' => 0,
        ))
          ->condition('type', array_keys($profile_types), 'IN')
          ->execute();

        // Ensure each profile's current revision is also disabled.
        $query = db_update('commerce_customer_profile_revision')
          ->fields(array(
          'status' => 0,
        ))
          ->where('revision_id IN (SELECT revision_id FROM {commerce_customer_profile} WHERE type IN (:profile_types))', array(
          ':profile_types' => array_keys($profile_types),
        ))
          ->execute();

        // Loop through and disable customer profile reference fields that may
        // correspond to the disabled profile types.
        foreach ($profile_types as $type => $profile_type) {
          foreach (field_read_fields(array(
            'type' => 'commerce_customer_profile_reference',
          )) as $field_name => $field) {

            // If this field references profiles of the disabled type...
            if ($field['settings']['profile_type'] == $type) {

              // Set it to inactive and save it.
              $field['active'] = 0;
              field_update_field($field);
            }
          }
        }
      }
    }
  }
}

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

/**
 * Implements hook_permission().
 */
function commerce_customer_permission() {
  $permissions = array(
    'administer customer profile types' => array(
      'title' => t('Administer customer profile types'),
      'description' => t('Allows users to add customer profile types and configure their fields.'),
      'restrict access' => TRUE,
    ),
  );
  $permissions += commerce_entity_access_permissions('commerce_customer_profile');
  return $permissions;
}

/**
 * Implements hook_theme().
 */
function commerce_customer_theme() {
  return array(
    'commerce_customer_profile' => array(
      'variables' => array(
        'profile' => NULL,
        'view_mode' => NULL,
      ),
    ),
  );
}

/**
 * Implements hook_commerce_customer_profile_type_info().
 */
function commerce_customer_commerce_customer_profile_type_info() {
  $profile_types = array();
  $profile_types['billing'] = array(
    'type' => 'billing',
    'name' => t('Billing information'),
    'description' => t('The profile used to collect billing information on the checkout and order forms.'),
    'help' => '',
  );
  return $profile_types;
}

/**
 * Implements hook_commerce_checkout_pane_info().
 */
function commerce_customer_commerce_checkout_pane_info() {
  $checkout_panes = array();
  $weight = 5;
  foreach (commerce_customer_profile_types() as $type => $profile_type) {

    // Get instance data for the customer profile reference field.
    $field_name = variable_get('commerce_customer_profile_' . $type . '_field', '');
    $instance = field_info_instance('commerce_order', $field_name, 'commerce_order');
    $translated_instance = commerce_i18n_object('field_instance', $instance);
    $checkout_panes['customer_profile_' . $type] = array(
      'title' => !empty($instance['label']) ? $translated_instance['label'] : $profile_type['name'],
      'file' => 'includes/commerce_customer.checkout_pane.inc',
      'base' => 'commerce_customer_profile_pane',
      'page' => !empty($instance) ? 'checkout' : 'disabled',
      'locked' => empty($instance),
      'weight' => isset($profile_type['checkout_pane_weight']) ? $profile_type['checkout_pane_weight'] : $weight++,
    );
  }
  return $checkout_panes;
}

/**
 * Implements hook_field_delete_instance().
 */
function commerce_customer_field_delete_instance($instance) {

  // If the checkout module is enabled, we need to react to the deletion of a
  // profile reference field from the core order bundle by updating the related
  // checkout pane so it is no longer configured to use the deleted field.
  if (module_exists('commerce_checkout')) {
    $field = field_info_field($instance['field_name']);

    // Ensure the deleted field instance is a customer profile reference field
    // and is attached to the core commerce_order entity type / bundle.
    if ($field['type'] == 'commerce_customer_profile_reference' && $instance['entity_type'] == 'commerce_order' && $instance['bundle'] == 'commerce_order') {

      // Loop through the customer profile types to see if there's a checkout
      // pane that used the deleted field.
      foreach (commerce_customer_profile_types() as $type => $profile_type) {
        if (variable_get('commerce_customer_profile_' . $type . '_field', '') == $field['field_name']) {

          // Unset the field variable and disable the checkout pane.
          variable_set('commerce_customer_profile_' . $type . '_field', '');
          $checkout_pane = commerce_checkout_pane_load('customer_profile_' . $type);
          $checkout_pane['enabled'] = FALSE;
          $checkout_pane['page'] = 'disabled';
          commerce_checkout_pane_save($checkout_pane);
          drupal_static_reset('commerce_checkout_panes');
        }
      }
    }
  }
}

/**
 * Returns an array of customer profile type arrays keyed by type.
 */
function commerce_customer_profile_types() {

  // First check the static cache for a profile types array.
  $profile_types =& drupal_static(__FUNCTION__);

  // If it did not exist, fetch the types now.
  if (!isset($profile_types)) {
    $profile_types = array();

    // Find profile types defined by hook_commerce_customer_profile_type_info().
    foreach (module_implements('commerce_customer_profile_type_info') as $module) {
      foreach (module_invoke($module, 'commerce_customer_profile_type_info') as $type => $profile_type) {

        // Initialize customer profile type properties if necessary.
        $defaults = array(
          'description' => '',
          'help' => '',
          'addressfield' => TRUE,
          'module' => $module,
          'label_callback' => 'commerce_customer_profile_default_label',
        );
        $profile_types[$type] = array_merge($defaults, $profile_type);
      }
    }

    // Last allow the info to be altered by other modules.
    drupal_alter('commerce_customer_profile_type_info', $profile_types);
  }
  return $profile_types;
}

/**
 * Loads a customer profile type.
 *
 * @param $type
 *   The machine-readable name of the customer profile type; accepts normal
 *     machine names and URL prepared machine names with underscores replaced by
 *     hyphens.
 */
function commerce_customer_profile_type_load($type) {
  $type = strtr($type, array(
    '-' => '_',
  ));
  $profile_types = commerce_customer_profile_types();
  return !empty($profile_types[$type]) ? $profile_types[$type] : FALSE;
}

/**
 * Resets the cached list of customer profile types.
 */
function commerce_customer_profile_types_reset() {
  $profile_types =& drupal_static('commerce_customer_profile_types');
  $profile_types = NULL;
  entity_info_cache_clear();
}

/**
 * Returns the human readable name of any or all customer profile types.
 *
 * @param $type
 *   Optional parameter specifying the type whose name to return.
 *
 * @return
 *   Either an array of all profile type names keyed by the machine name or a
 *     string containing the human readable name for the specified type. If a
 *     type is specified that does not exist, this function returns FALSE.
 */
function commerce_customer_profile_type_get_name($type = NULL) {
  $profile_types = commerce_customer_profile_types();

  // Return a type name if specified and it exists.
  if (!empty($type)) {
    if (isset($profile_types[$type])) {
      return $profile_types[$type]['name'];
    }
    else {

      // Return FALSE if it does not exist.
      return FALSE;
    }
  }

  // Otherwise turn the array values into the type name only.
  foreach ($profile_types as $key => $value) {
    $profile_types[$key] = $value['name'];
  }
  return $profile_types;
}

/**
 * Wraps commerce_customer_profile_type_get_name() for the Entity module.
 */
function commerce_customer_profile_type_options_list() {
  return commerce_customer_profile_type_get_name();
}

/**
 * Title callback: return the human-readable customer profile type name.
 */
function commerce_customer_profile_type_title($profile_type) {
  return $profile_type['name'];
}

/**
 * Returns a path argument from a customer profile type.
 */
function commerce_customer_profile_type_to_arg($type) {
  return $type;
}

/**
 * Returns an initialized customer profile object.
 *
 * @param $type
 *   The type of customer profile to create.
 * @param $uid
 *   The uid of the user the customer profile is for.
 *
 * @return
 *   A customer profile object with all default fields initialized.
 */
function commerce_customer_profile_new($type = '', $uid = 0) {
  return entity_get_controller('commerce_customer_profile')
    ->create(array(
    'type' => $type,
    'uid' => $uid,
  ));
}

/**
 * Saves a customer profile.
 *
 * @param $profile
 *   The full customer profile object to save. If $profile->profile_id is empty,
 *     a new customer profile will be created.
 *
 * @return
 *   SAVED_NEW or SAVED_UPDATED depending on the operation performed.
 */
function commerce_customer_profile_save($profile) {
  return entity_get_controller('commerce_customer_profile')
    ->save($profile);
}

/**
 * Loads a customer profile by ID.
 */
function commerce_customer_profile_load($profile_id) {
  $profiles = commerce_customer_profile_load_multiple(array(
    $profile_id,
  ), array());
  return $profiles ? reset($profiles) : FALSE;
}

/**
 * Loads multiple customer profiles by ID or based on a set of conditions.
 *
 * @see entity_load()
 *
 * @param $profile_ids
 *   An array of customer profile IDs.
 * @param $conditions
 *   An array of conditions on the {commerce_customer_profile} table in the form
 *     'field' => $value.
 * @param $reset
 *   Whether to reset the internal customer profile loading cache.
 *
 * @return
 *   An array of customer profile objects indexed by profile_id.
 */
function commerce_customer_profile_load_multiple($profile_ids = array(), $conditions = array(), $reset = FALSE) {
  return entity_load('commerce_customer_profile', $profile_ids, $conditions, $reset);
}

/**
 * Determines whether or not the give customer profile can be deleted.
 *
 * @param $profile
 *   The customer profile to be checked for deletion.
 *
 * @return
 *   Boolean indicating whether or not the customer profile can be deleted.
 */
function commerce_customer_profile_can_delete($profile) {

  // Return FALSE if the given profile does not have an ID; it need not be
  // deleted, which is functionally equivalent to cannot be deleted as far as
  // code depending on this function is concerned.
  if (empty($profile->profile_id)) {
    return FALSE;
  }

  // If any module implementing hook_commerce_customer_profile_can_delete()
  // returns FALSE the customer profile cannot be deleted. Return TRUE if none
  // return FALSE.
  return !in_array(FALSE, module_invoke_all('commerce_customer_profile_can_delete', $profile));
}

/**
 * Deletes a customer profile by ID.
 *
 * @param $profile_id
 *   The ID of the customer profile to delete.
 * @param $entity_context
 *   An optional entity context array that specifies the entity through whose
 *   customer profile reference field the given profile is being deleted:
 *   - entity_type: The type of entity.
 *   - entity_id: The unique ID of the entity.
 *
 * @return
 *   TRUE on success, FALSE otherwise.
 */
function commerce_customer_profile_delete($profile_id, $entity_context = array()) {
  return commerce_customer_profile_delete_multiple(array(
    $profile_id,
  ), $entity_context);
}

/**
 * Deletes multiple customer profiles by ID.
 *
 * @param $profile_ids
 *   An array of customer profile IDs to delete.
 * @param $entity_context
 *   An optional entity context array that specifies the entity through whose
 *   customer profile reference field the given profiles are being deleted:
 *   - entity_type: The type of entity.
 *   - entity_id: The unique ID of the entity.
 *
 * @return
 *   TRUE on success, FALSE otherwise.
 */
function commerce_customer_profile_delete_multiple($profile_ids, $entity_context = array()) {
  return entity_get_controller('commerce_customer_profile')
    ->delete($profile_ids, NULL, $entity_context);
}

/**
 * Implements hook_commerce_customer_profile_delete().
 *
 * Remove references to this customer profile in all customer profile reference
 * field contents.
 */
function commerce_customer_commerce_customer_profile_delete($profile) {

  // Check the data in every customer profile reference field.
  foreach (commerce_info_fields('commerce_customer_profile_reference') as $field_name => $field) {

    // Query for any entity referencing the deleted profile in this field.
    $query = new EntityFieldQuery();
    $query
      ->fieldCondition($field_name, 'profile_id', $profile->profile_id, '=');
    $result = $query
      ->execute();

    // If results were returned...
    if (!empty($result)) {

      // Loop over results for each type of entity returned.
      foreach ($result as $entity_type => $data) {

        // Load the entities of the current type.
        $entities = entity_load($entity_type, array_keys($data));

        // Loop over each entity and remove the reference to the deleted profile.
        foreach ($entities as $entity_id => $entity) {
          commerce_entity_reference_delete($entity, $field_name, 'profile_id', $profile->profile_id);

          // Store the changes to the entity.
          entity_save($entity_type, $entity);
        }
      }
    }
  }
}

/**
 * Checks customer profile access for various operations.
 *
 * @param $op
 *   The operation being performed. One of 'view', 'update', 'create' or
 *   'delete'.
 * @param $profile
 *   Optionally a profile to check access for or for the create operation the
 *   profile type. If nothing is given access permissions for all profiles are returned.
 * @param $account
 *   The user to check for. Leave it to NULL to check for the current user.
 */
function commerce_customer_profile_access($op, $profile = NULL, $account = NULL) {
  return commerce_entity_access($op, $profile, $account, 'commerce_customer_profile');
}

/**
 * Implements hook_query_TAG_alter().
 */
function commerce_customer_query_commerce_customer_profile_access_alter(QueryAlterableInterface $query) {
  return commerce_entity_access_query_alter($query, 'commerce_customer_profile');
}

/**
 * Implements hook_field_info().
 */
function commerce_customer_field_info() {
  return array(
    'commerce_customer_profile_reference' => array(
      'label' => t('Customer profile reference'),
      'description' => t('This field stores the ID of a related customer profile as an integer value.'),
      'settings' => array(
        'profile_type' => 'billing',
        'options_list_limit' => 50,
      ),
      'instance_settings' => array(),
      'default_widget' => 'options_select',
      'default_formatter' => 'commerce_customer_profile_reference_display',
      'property_type' => 'commerce_customer_profile',
      'property_callbacks' => array(
        'commerce_customer_profile_property_info_callback',
      ),
    ),
  );
}

/**
 * Implements hook_field_settings_form().
 */
function commerce_customer_field_settings_form($field, $instance, $has_data) {
  $settings = $field['settings'];
  $form = array();
  if ($field['type'] == 'commerce_customer_profile_reference') {
    $options = array();

    // Build an options array of the customer profile types.
    foreach (commerce_customer_profile_type_get_name() as $type => $name) {
      $options[$type] = check_plain($name);
    }
    $form['profile_type'] = array(
      '#type' => 'radios',
      '#title' => t('Customer profile type that can be referenced'),
      '#options' => $options,
      '#default_value' => !empty($settings['profile_type']) ? $settings['profile_type'] : 'billing',
      '#disabled' => $has_data,
    );
    $form['options_list_limit'] = array(
      '#type' => 'textfield',
      '#title' => t('Options list limit'),
      '#description' => t('Limits the number of customer profiles available in field widgets with options lists; leave blank for no limit.'),
      '#default_value' => !empty($settings['options_list_limit']) ? $settings['options_list_limit'] : 50,
      '#element_validate' => array(
        'commerce_options_list_limit_validate',
      ),
    );
  }
  return $form;
}

/**
 * Implements hook_field_validate().
 *
 * Possible error codes:
 * - 'invalid_profile_id': profile_id is not valid for the field (not a valid
 *   line item ID).
 */
function commerce_customer_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
  $translated_instance = commerce_i18n_object('field_instance', $instance);
  if ($field['type'] == 'commerce_customer_profile_reference') {

    // Extract profile_ids to check.
    $profile_ids = array();

    // First check non-numeric profile_id's to avoid losing time with them.
    foreach ($items as $delta => $item) {
      if (is_array($item) && !empty($item['profile_id'])) {
        if (is_numeric($item['profile_id'])) {
          $profile_ids[] = $item['profile_id'];
        }
        else {
          $errors[$field['field_name']][$langcode][$delta][] = array(
            'error' => 'invalid_profile_id',
            'message' => t('%name: you have specified an invalid customer profile for this reference field.', array(
              '%name' => $translated_instance['label'],
            )),
          );
        }
      }
    }

    // Prevent performance hog if there are no ids to check.
    if ($profile_ids) {
      $profiles = commerce_customer_profile_load_multiple($profile_ids, array(
        'type' => $field['settings']['profile_type'],
      ));
      foreach ($items as $delta => $item) {
        if (is_array($item)) {

          // Check that the item specifies a profile_id and that a profile of
          // the proper type exists with that ID.
          if (!empty($item['profile_id']) && !isset($profiles[$item['profile_id']])) {
            $errors[$field['field_name']][$langcode][$delta][] = array(
              'error' => 'invalid_profile_id',
              'message' => t('%name: you have specified an invalid customer profile for this reference field.', array(
                '%name' => $translated_instance['label'],
              )),
            );
          }
        }
      }
    }
  }
}

/**
 * Implements hook_field_is_empty().
 */
function commerce_customer_field_is_empty($item, $field) {
  if ($field['type'] == 'commerce_customer_profile_reference') {

    // profile_id = 0 is empty too, which is exactly what we want.
    return empty($item['profile_id']);
  }
}

/**
 * Implements hook_field_formatter_info().
 */
function commerce_customer_field_formatter_info() {
  return array(
    'commerce_customer_profile_reference_display' => array(
      'label' => t('Customer profile display'),
      'description' => t('Display the customer profile.'),
      'field types' => array(
        'commerce_customer_profile_reference',
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function commerce_customer_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $result = array();
  switch ($display['type']) {
    case 'commerce_customer_profile_reference_display':
      foreach ($items as $delta => $item) {
        $profile = commerce_customer_profile_load($item['profile_id']);
        if ($profile) {
          $content = entity_view('commerce_customer_profile', array(
            $profile->profile_id => $profile,
          ), 'customer', $langcode);
          $result[$delta] = array(
            '#markup' => drupal_render($content),
          );
        }
      }
      break;
  }
  return $result;
}

/**
 * Implements hook_field_widget_info().
 *
 * Defines widgets available for use with field types as specified in each
 * widget's $info['field types'] array.
 */
function commerce_customer_field_widget_info() {
  $widgets = array();

  // Define the creation / reference widget for line items.
  $widgets['commerce_customer_profile_manager'] = array(
    'label' => t('Customer profile manager'),
    'description' => t('Use a complex widget to edit the profile referenced by this object.'),
    'field types' => array(
      'commerce_customer_profile_reference',
    ),
    'settings' => array(),
    'behaviors' => array(
      'multiple values' => FIELD_BEHAVIOR_CUSTOM,
      'default value' => FIELD_BEHAVIOR_NONE,
    ),
  );
  return $widgets;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function commerce_customer_form_field_ui_field_edit_form_alter(&$form, &$form_state) {

  // Alter the field edit form so it's obvious that customer profile manager
  // widgets do not support multiple values.
  if (empty($form['locked']) && !empty($form['instance']) && $form['instance']['widget']['type']['#value'] == 'commerce_customer_profile_manager') {
    $form['field']['cardinality']['#options'] = array(
      '1' => '1',
    );
    $form['field']['cardinality']['#description'] = t('The customer profile manager widget only supports single value editing and entry via its form.');
  }
}

/**
 * Implements hook_field_widget_info_alter().
 */
function commerce_customer_field_widget_info_alter(&$info) {
  if (!empty($info['options_select'])) {
    $info['options_select']['field types'][] = 'commerce_customer_profile_reference';
  }
}

/**
 * Implements hook_options_list().
 */
function commerce_customer_options_list($field) {
  $options = array();

  // Look for an options list limit in the field settings.
  if (!empty($field['settings']['options_list_limit'])) {
    $limit = (int) $field['settings']['options_list_limit'];
  }
  else {
    $limit = NULL;
  }

  // Loop through all customer matches.
  foreach (commerce_customer_match_customer_profiles($field, array(), $limit) as $profile_id => $data) {

    // Add them to the options list in optgroups by customer profile type.
    $name = check_plain(commerce_customer_profile_type_get_name($data['type']));
    $options[$name][$profile_id] = t('@profile: User @user', array(
      '@profile' => $profile_id,
      '@user' => $data['uid'],
    ));
  }

  // Simplify the options list if only one optgroup exists.
  if (count($options) == 1) {
    $options = reset($options);
  }
  return $options;
}

/**
 * Implements hook_field_widget_form().
 *
 * Used to define the form element for custom widgets.
 */
function commerce_customer_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {

  // Define the complex customer profile reference field widget.
  if ($instance['widget']['type'] == 'commerce_customer_profile_manager') {
    $profile_type = commerce_customer_profile_type_load($field['settings']['profile_type']);

    // Do not attempt to render the widget for a non-existent profile type.
    if (empty($profile_type)) {
      drupal_set_message(t('Field %field_name attempted to use the non-existing customer profile type %type.', array(
        '%field_name' => $field['field_name'],
        '%type' => $field['settings']['profile_type'],
      )), 'error');
      return array();
    }

    // Build an array of customer profile IDs from this field's values.
    $profile_ids = array();
    foreach ($items as $item) {
      $profile_ids[] = $item['profile_id'];
    }

    // Load the profiles for temporary storage in the form array.
    $profiles = commerce_customer_profile_load_multiple($profile_ids);
    if (empty($profiles)) {
      if (!empty($form_state['add_profile_' . $profile_type['type']])) {

        // If the "Add profile" button is clicked, create a new profile.
        array_push($profiles, commerce_customer_profile_new($profile_type['type']));
      }
      else {

        // Otherwise add an empty array to make the "Add profile" button show up.
        array_push($profiles, array());
      }
    }

    // Update the base form element array to use the proper validate function.
    $element += array(
      '#element_validate' => array(
        'commerce_customer_profile_manager_validate',
      ),
      'profiles' => array(
        '#tree' => TRUE,
      ),
    );

    // Add a set of elements to the form for each referenced profile.
    $key = 0;
    foreach ($profiles as $profile) {
      $wrapper_id = 'replace_' . $key . '_' . $profile_type['type'];
      $element['profiles'][$key] = array(
        '#type' => 'fieldset',
        '#title' => check_plain($profile_type['name']),
        '#parents' => array_merge($element['#field_parents'], array(
          $element['#field_name'],
          $langcode,
          'profiles',
          $key,
        )),
        '#prefix' => '<div id="' . $wrapper_id . '">',
        '#suffix' => '</div>',
      );
      if (empty($profile)) {

        // There's no profile of this type for this order, show an "add
        // profile" button.
        $element['profiles'][$key]['actions']['add_' . $profile_type['type']] = array(
          '#type' => 'submit',
          '#value' => t('Add @type', array(
            '@type' => strtolower($profile_type['name']),
          )),
          '#submit' => array(
            'commerce_customer_add_profile',
          ),
          '#limit_validation_errors' => array(
            array_merge($element['#field_parents'], array(
              $element['#field_name'],
            )),
          ),
          '#ajax' => array(
            'callback' => 'commerce_customer_add_profile_callback',
            'method' => 'replace',
            'wrapper' => $wrapper_id,
          ),
          '#commerce_customer_profile_type' => $profile_type['type'],
        );
        continue;
      }

      // Store the original customer profile for later comparison.
      $element['profiles'][$key]['profile'] = array(
        '#type' => 'value',
        '#value' => $profile,
      );
      field_attach_form('commerce_customer_profile', $profile, $element['profiles'][$key], $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) {

        // First make sure this addressfield is part of the current profile.
        if (!empty($element['profiles'][$key][$field_name]['#language'])) {
          $langcode = $element['profiles'][$key][$field_name]['#language'];

          // Only consider this addressfield if it's represented on the form.
          if (!empty($element['profiles'][$key][$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($element['profiles'][$key][$field_name][$langcode]) as $delta) {
          if ($element['profiles'][$key][$field_name][$langcode][$delta]['#type'] != 'submit') {
            $element['profiles'][$key][$field_name][$langcode][$delta]['#type'] = 'container';
          }
        }

        // Remove the default #parents array so the normal tree can do its thing.
        unset($element['profiles'][$key]['#parents']);
      }

      // This checkbox will be overridden with a clickable delete image.
      // TODO: Make this an #ajaxy submit button.
      if ($profile->profile_id) {

        // Create a title for this box based on whether or not the currently
        // referenced customer profile can be deleted.
        if (commerce_customer_profile_can_delete($profile)) {
          $title = t('Delete this profile');
        }
        else {
          $title = t('Clear this profile');
        }
        $element['profiles'][$key]['remove'] = array(
          '#type' => 'checkbox',
          '#title' => $title,
          '#default_value' => FALSE,
          '#access' => commerce_customer_profile_access('delete', $profile),
          '#weight' => 100,
        );
      }
      else {

        // Entity is not saved yet, show a "cancel" button.
        $element['profiles'][$key]['actions']['cancel_' . $profile_type['type']] = array(
          '#type' => 'submit',
          '#value' => t('Cancel'),
          '#submit' => array(
            'commerce_customer_cancel_profile',
          ),
          '#limit_validation_errors' => array(
            array_merge($element['#field_parents'], array(
              $element['#field_name'],
            )),
          ),
          '#ajax' => array(
            'callback' => 'commerce_customer_add_profile_callback',
            'method' => 'replace',
            'wrapper' => $wrapper_id,
          ),
          '#commerce_customer_profile_type' => $profile_type['type'],
        );
      }
      $key += 1;
    }

    // If the reference field is not required, unrequire any elements in the
    // profile edit form.
    if (!$delta == 0 || !$instance['required']) {
      commerce_unrequire_form_elements($element);
    }
    return $element;
  }
}

/**
 * Submit callback to add a new customer profile to the order.
 */
function commerce_customer_add_profile($form, &$form_state) {
  $profile_type = $form_state['triggering_element']['#commerce_customer_profile_type'];
  $form_state['add_profile_' . $profile_type] = TRUE;
  $form_state['rebuild'] = TRUE;
}

/**
 * Submit callback to cancel adding a new customer profile to the order.
 */
function commerce_customer_cancel_profile($form, &$form_state) {
  $profile_type = $form_state['triggering_element']['#commerce_customer_profile_type'];
  $form_state['add_profile_' . $profile_type] = FALSE;
  $form_state['rebuild'] = TRUE;
}

/**
 * Ajax callback to select and return only the form elements that will replace
 * the add profile button.
 */
function commerce_customer_add_profile_callback($form, &$form_state) {

  // Reverse the array parents of the triggering element, because we know the
  // field name is 5 elements up from the triggering element.
  $parents = array_reverse($form_state['triggering_element']['#array_parents']);
  $field_name = $parents[5];
  $langcode = $form[$field_name]['#language'];
  return $form[$field_name][$langcode];
}

/**
 * Validation callback for a commerce_customer_profile_manager element.
 *
 * When the form is submitted, the profile reference field stores the profile
 * IDs as derived from the $element['profiles'] array and updates any
 * referenced profiles based on the extra form elements.
 */
function commerce_customer_profile_manager_validate($element, &$form_state, $form) {
  $value = array();
  $triggering_element = $form_state['triggering_element'];

  // If the triggering element is not the "add profile" button and it wants to
  // limit validation errors and the form is not going to be submitted...
  if (!isset($triggering_element['#commerce_customer_profile_type']) && isset($triggering_element['#limit_validation_errors']) && $triggering_element['#limit_validation_errors'] !== FALSE && !($form_state['submitted'] && !isset($triggering_element['#submit']))) {

    // Ensure this element wasn't specifically marked for validation in the
    // #limit_validation_errors sections array.
    $section_match = FALSE;
    foreach ($triggering_element['#limit_validation_errors'] as $section) {

      // Because #limit_validation_errors sections force validation for any
      // element that matches the section or is a child of it, we can consider
      // it a match if the section completely matches the beginning of this
      // element's #parents array even if #parents contains additional elements.
      if (array_intersect_assoc($section, $element['#parents']) === $section) {
        $section_match = TRUE;
      }
    }

    // Exit this validate function, because the form is going to be rebuilt and
    // the data submitted may very well be incomplete.
    if (!$section_match) {
      form_set_value($element, array(), $form_state);
      return;
    }
  }

  // Loop through the profiles in the manager table.
  foreach (element_children($element['profiles']) as $key) {
    if (!isset($element['profiles'][$key]['profile'])) {

      // There's no profile of this type.
      continue;
    }

    // Update the profile based on the values in the additional elements.
    $profile = clone $element['profiles'][$key]['profile']['#value'];
    $cancel = $triggering_element['#parents'][count($triggering_element['#parents']) - 1] === 'cancel_' . $profile->type;

    // If the profile adding was canceled or it has been marked for deletion...
    if ($cancel || $profile->profile_id && $element['profiles'][$key]['remove']['#value']) {

      // Delete the profile now if we can and don't include it in the $value array.
      if (commerce_customer_profile_can_delete($profile)) {

        // If another module altered in an entity context, be sure to pass it to
        // the delete function.
        if (!empty($profile->entity_context)) {
          commerce_customer_profile_delete($profile->profile_id, $profile->entity_context);
        }
        else {
          commerce_customer_profile_delete($profile->profile_id);
        }
      }
    }
    else {

      // Notify field widgets to validate their data.
      field_attach_form_validate('commerce_customer_profile', $profile, $element['profiles'][$key], $form_state);

      // TODO: Trap it on error, rebuild the form with error messages.
      // Notify field widgets to save the field data.
      field_attach_submit('commerce_customer_profile', $profile, $element['profiles'][$key], $form_state);

      // Only save if values were actually changed.
      if ($profile != $element['profiles'][$key]['profile']['#value']) {
        commerce_customer_profile_save($profile);
      }

      // Add the profile ID to the current value of the reference field.
      $value[] = array(
        'profile_id' => $profile->profile_id,
      );
    }
  }
  form_set_value($element, $value, $form_state);
}

/**
 * Implements hook_field_widget_error().
 */
function commerce_customer_field_widget_error($element, $error) {
  form_error($element, $error['message']);
}

/**
 * Callback to alter the property info of the reference field.
 *
 * @see commerce_customer_field_info().
 */
function commerce_customer_profile_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
  $property =& $info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']];
  $property['options list'] = 'entity_metadata_field_options_list';
}

/**
 * Fetches an array of all customer profiles matching the given parameters.
 *
 * This info is used in various places (allowed values, autocomplete results,
 * input validation...). Some of them only need the profile_ids, others
 * profile_id + titles, others yet profile_id + titles + rendered row (for
 * display in widgets).
 *
 * The array we return contains all the potentially needed information,
 * and lets calling functions use the parts they actually need.
 *
 * @param $field
 *   The field description.
 * @param $ids
 *   Optional product ids to lookup.
 * @param $limit
 *   If non-zero, limit the size of the result set.
 *
 * @return
 *   An array of valid profiles in the form:
 *   array(
 *     profile_id => array(
 *       'uid' => The user ID,
 *       'rendered' => The text to display in widgets (can be HTML)
 *     ),
 *     ...
 *   )
 */
function commerce_customer_match_customer_profiles($field, $ids = array(), $limit = NULL) {
  $results =& drupal_static(__FUNCTION__, array());

  // Create unique id for static cache.
  $cid = implode(':', array(
    $field['field_name'],
    implode('-', $ids),
    $limit,
  ));
  if (!isset($results[$cid])) {
    $matches = _commerce_customer_match_customer_profiles_standard($field, $ids, $limit);

    // Store the results.
    $results[$cid] = !empty($matches) ? $matches : array();
  }
  return $results[$cid];
}

/**
 * Helper function for commerce_customer_match_customer_profiles().
 *
 * Returns an array of products matching the specific parameters.
 */
function _commerce_customer_match_customer_profiles_standard($field, $ids = array(), $limit = NULL) {

  // Build the query object with the necessary fields.
  $query = db_select('commerce_customer_profile', 'cp');
  $profile_id_alias = $query
    ->addField('cp', 'profile_id');
  $profile_uid_alias = $query
    ->addField('cp', 'uid');
  $profile_type_alias = $query
    ->addField('cp', 'type');

  // Add a condition to the query to filter by matching profile types.
  if (!empty($field['settings']['referenceable_types']) && is_array($field['settings']['referenceable_types'])) {
    $types = array_diff(array_values($field['settings']['referenceable_types']), array(
      0,
      NULL,
    ));

    // Only filter by type if some types have been specified.
    if (!empty($types)) {
      $query
        ->condition('cp.type', $types, 'IN');
    }
  }
  if ($ids) {

    // Otherwise add a profile_id specific condition if specified.
    $query
      ->condition($profile_id_alias, $ids, 'IN');
  }

  // Order the results by ID and then profile type.
  $query
    ->orderBy($profile_id_alias)
    ->orderBy($profile_type_alias);

  // Add a limit if specified.
  if ($limit) {
    $query
      ->range(0, $limit);
  }

  // Execute the query and build the results array.
  $result = $query
    ->execute();
  $matches = array();
  foreach ($result
    ->fetchAll() as $profile) {
    $matches[$profile->profile_id] = array(
      'uid' => $profile->uid,
      'type' => $profile->type,
      'rendered' => t('Profile @profile_id', array(
        '@profile_id' => $profile->profile_id,
      )),
    );
  }
  return $matches;
}

/**
 * Callback for getting customer profile properties.
 *
 * @see commerce_customer_entity_property_info()
 */
function commerce_customer_profile_get_properties($profile, array $options, $name) {
  switch ($name) {
    case 'user':
      return $profile->uid;
  }
}

/**
 * Callback for setting customer profile properties.
 *
 * @see commerce_customer_entity_property_info()
 */
function commerce_customer_profile_set_properties($profile, $name, $value) {
  if ($name == 'user') {
    $profile->uid = $value;
  }
}

/**
 * Element validate callback: Pertaining to the "copy profile" checkbox.
 */
function commerce_customer_profile_copy_validate($element, &$form_state, $form) {
  $triggering_element = end($form_state['triggering_element']['#array_parents']);
  $pane_id = reset($element['#array_parents']);

  // If this profile copying checkbox was unchecked, update and save the order.
  if ($triggering_element == 'commerce_customer_profile_copy' && $form_state['triggering_element']['#id'] == $element['#id'] && empty($element['#value'])) {
    $form_state['order']->data['profile_copy'][$pane_id]['status'] = FALSE;
    unset($form_state['order']->data['profile_copy'][$pane_id]['elements']);
    commerce_order_save($form_state['order']);
  }
  elseif (!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,
    );

    // Try getting the source profile from the form_state values, if it is present on the form..
    if (isset($form_state['values'][$source_id])) {

      // If there were errors in the source profile pane, there's no need
      // to copy the profile fields, we should therefore stop here.
      if ($errors = form_get_errors()) {
        foreach ($errors as $field => $error) {
          if (strpos($field, $source_id . '][') === 0) {
            return;
          }
        }
      }
      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 {

      // Check for source profile via order wrapper.
      $wrapper = entity_metadata_wrapper('commerce_order', $form_state['order']);
      $profile = NULL;
      if ($source_field_name = variable_get('commerce_' . $source_id . '_field', '')) {
        $profile = $wrapper->{$source_field_name}
          ->value();
      }
      elseif (!empty($form_state['order']->data['profiles'][$source_id])) {
        $profile = commerce_customer_profile_load($form_state['order']->data['profiles'][$source_id]);
      }
      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]);
        }
      }
    }
  }
}

/**
 * Copy field values from a source profile to a target array.
 *
 * @param $info
 *   An array containing info for the entity type, bundle, and pane ID.
 * @param $target
 *   An array (typically $form_state) in which values will be copied to.
 * @param $source
 *   Can be either an array or object of values.
 * @param $form_state
 *   The form state array from the form.
 */
function commerce_customer_profile_copy_fields($info, &$target, $source, &$form_state) {
  list($entity_type, $bundle, $pane_id) = $info;
  $form_state['order']->data['profile_copy'][$pane_id]['elements'] = array();

  // Loop over all the field instances that could be attached to this entity.
  foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) {
    $field = NULL;

    // Extract the field value from the source object or array.
    if (is_object($source) && isset($source->{$field_name})) {
      $field = $source->{$field_name};
    }
    elseif (is_array($source) && isset($source[$field_name])) {
      $field = $source[$field_name];
    }

    // Loop over the source field value and copy its items to the target.
    if (is_array($field)) {
      foreach ($field as $langcode => $items) {
        if (is_array($items)) {
          $target[$field_name][$langcode] = array();
          foreach ($items as $delta => $item) {
            $target[$field_name][$langcode][$delta] = $item;
            $form_state['order']->data['profile_copy'][$pane_id]['elements'][$field_name][$langcode][$delta] = TRUE;
          }
        }
        else {
          $target[$field_name][$langcode] = $items;
          $form_state['order']->data['profile_copy'][$pane_id]['elements'][$field_name][$langcode] = TRUE;
        }
      }
    }
  }
}

Functions

Namesort descending Description
commerce_customer_add_profile Submit callback to add a new customer profile to the order.
commerce_customer_add_profile_callback Ajax callback to select and return only the form elements that will replace the add profile button.
commerce_customer_cancel_profile Submit callback to cancel adding a new customer profile to the order.
commerce_customer_commerce_checkout_pane_info Implements hook_commerce_checkout_pane_info().
commerce_customer_commerce_customer_profile_delete Implements hook_commerce_customer_profile_delete().
commerce_customer_commerce_customer_profile_type_info Implements hook_commerce_customer_profile_type_info().
commerce_customer_configure_customer_fields Configures fields referencing customer profile types defined by enabled modules and configures the fields on those profile types if necessary.
commerce_customer_configure_customer_profile_type Ensures the address field is present on the specified customer profile bundle.
commerce_customer_configure_customer_types Configures customer profile types defined by enabled modules.
commerce_customer_enable Implements hook_enable().
commerce_customer_entity_info Implements hook_entity_info().
commerce_customer_field_delete_instance Implements hook_field_delete_instance().
commerce_customer_field_formatter_info Implements hook_field_formatter_info().
commerce_customer_field_formatter_view Implements hook_field_formatter_view().
commerce_customer_field_info Implements hook_field_info().
commerce_customer_field_is_empty Implements hook_field_is_empty().
commerce_customer_field_settings_form Implements hook_field_settings_form().
commerce_customer_field_validate Implements hook_field_validate().
commerce_customer_field_widget_error Implements hook_field_widget_error().
commerce_customer_field_widget_form Implements hook_field_widget_form().
commerce_customer_field_widget_info Implements hook_field_widget_info().
commerce_customer_field_widget_info_alter Implements hook_field_widget_info_alter().
commerce_customer_form_field_ui_field_edit_form_alter Implements hook_form_FORM_ID_alter().
commerce_customer_hook_info Implements hook_hook_info().
commerce_customer_match_customer_profiles Fetches an array of all customer profiles matching the given parameters.
commerce_customer_modules_disabled Implements hook_modules_disabled().
commerce_customer_modules_enabled Implements hook_modules_enabled().
commerce_customer_options_list Implements hook_options_list().
commerce_customer_permission Implements hook_permission().
commerce_customer_profile_access Checks customer profile access for various operations.
commerce_customer_profile_can_delete Determines whether or not the give customer profile can be deleted.
commerce_customer_profile_copy_fields Copy field values from a source profile to a target array.
commerce_customer_profile_copy_validate Element validate callback: Pertaining to the "copy profile" checkbox.
commerce_customer_profile_default_label Returns the default label for a customer profile.
commerce_customer_profile_delete Deletes a customer profile by ID.
commerce_customer_profile_delete_multiple Deletes multiple customer profiles by ID.
commerce_customer_profile_get_properties Callback for getting customer profile properties.
commerce_customer_profile_label Entity label callback: returns the label for an individual customer profile.
commerce_customer_profile_load Loads a customer profile by ID.
commerce_customer_profile_load_multiple Loads multiple customer profiles by ID or based on a set of conditions.
commerce_customer_profile_manager_validate Validation callback for a commerce_customer_profile_manager element.
commerce_customer_profile_new Returns an initialized customer profile object.
commerce_customer_profile_property_info_callback Callback to alter the property info of the reference field.
commerce_customer_profile_save Saves a customer profile.
commerce_customer_profile_set_properties Callback for setting customer profile properties.
commerce_customer_profile_types Returns an array of customer profile type arrays keyed by type.
commerce_customer_profile_types_reset Resets the cached list of customer profile types.
commerce_customer_profile_type_get_name Returns the human readable name of any or all customer profile types.
commerce_customer_profile_type_load Loads a customer profile type.
commerce_customer_profile_type_options_list Wraps commerce_customer_profile_type_get_name() for the Entity module.
commerce_customer_profile_type_title Title callback: return the human-readable customer profile type name.
commerce_customer_profile_type_to_arg Returns a path argument from a customer profile type.
commerce_customer_profile_uri Entity uri callback: gives modules a chance to specify a path for a customer profile.
commerce_customer_query_commerce_customer_profile_access_alter Implements hook_query_TAG_alter().
commerce_customer_theme Implements hook_theme().
commerce_customer_views_api Implements hook_views_api().
_commerce_customer_match_customer_profiles_standard Helper function for commerce_customer_match_customer_profiles().