commerce_addressbook.module in Commerce Addressbook 7.2
Same filename and directory in other branches
Defines addressbook functionality for customer profiles, allowing them to be reused and managed on a per-user basis.
File
commerce_addressbook.moduleView 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
Name | 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(). |