'Store', 'description' => 'Administer orders, products, customers, store settings, etc.', 'page callback' => 'uc_store_admin', 'access callback' => 'uc_store_admin_access', 'weight' => -12, 'file' => 'uc_store.admin.inc', ); $items['admin/store/reports'] = array( 'title' => 'Reports', 'description' => 'Browse various store reports.', 'page callback' => 'uc_store_reports', 'access arguments' => array('view reports'), 'weight' => 2, 'file' => 'uc_store.admin.inc', 'position' => 'right', ); $items['admin/store/settings'] = array( 'title' => 'Configuration', 'description' => 'Adjust configuration settings for Ubercart.', 'page callback' => 'uc_store_configuration_page', 'access arguments' => array('administer store'), 'weight' => 6, 'file' => 'uc_store.admin.inc', 'position' => 'right', ); $items['admin/store/settings/countries'] = array( 'title' => 'Countries and addresses', 'description' => 'Manage available countries and configure address formats.', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_country_import_form'), 'access arguments' => array('administer store'), 'file' => 'uc_store.countries.inc', ); $items['admin/store/settings/countries/import'] = array( 'title' => 'Countries', 'description' => 'Import and manage countries.', 'access arguments' => array('administer store'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'file' => 'uc_store.countries.inc', ); $items['admin/store/settings/countries/fields'] = array( 'title' => 'Address fields', 'description' => 'Edit the address field settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_store_address_fields_form'), 'access arguments' => array('administer store'), 'type' => MENU_LOCAL_TASK, 'weight' => 1, 'file' => 'uc_store.admin.inc', ); $items['admin/store/settings/countries/formats'] = array( 'title' => 'Address formats', 'description' => 'Edit country specific address format settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_country_formats_form'), 'access arguments' => array('administer store'), 'type' => MENU_LOCAL_TASK, 'weight' => 1, 'file' => 'uc_store.countries.inc', ); $items['admin/store/settings/store'] = array( 'title' => 'Store', 'description' => 'Configure basic store settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_store_settings_form'), 'access arguments' => array('administer store'), 'file' => 'uc_store.admin.inc', 'weight' => -1, ); $items['admin/store/settings/countries/%/disable'] = array( 'title' => 'Disable a country', 'description' => 'Disable a country from use.', 'page callback' => '_uc_country_perform_country_action', 'page arguments' => array('uc_country_disable', 4), 'access arguments' => array('administer store'), 'type' => MENU_CALLBACK, 'file' => 'uc_store.countries.inc', ); $items['admin/store/settings/countries/%/enable'] = array( 'title' => 'Enable a country', 'description' => 'Enable a disabled country.', 'page callback' => '_uc_country_perform_country_action', 'page arguments' => array('uc_country_enable', 4), 'access arguments' => array('administer store'), 'type' => MENU_CALLBACK, 'file' => 'uc_store.countries.inc', ); $items['admin/store/settings/countries/%/remove'] = array( 'title' => 'Remove a country', 'description' => 'Remove an installed country.', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_country_remove_form', 4), 'access arguments' => array('administer store'), 'file' => 'uc_store.countries.inc', ); $items['admin/store/settings/countries/%/update/%'] = array( 'title' => 'Update a country', 'description' => 'Update an installed country.', 'page callback' => '_uc_country_perform_country_action', 'page arguments' => array('uc_country_update', 4, 6), 'access arguments' => array('administer store'), 'type' => MENU_CALLBACK, 'file' => 'uc_store.countries.inc', ); return $items; } /** * Access callback for top-level store administration menu item. */ function uc_store_admin_access() { return user_access('administer_store') || user_access('view all orders') || user_access('view customers') || user_access('administer products') || user_access('view reports'); } /** * Implements hook_init(). */ function uc_store_init() { module_load_include('inc', 'uc_store', 'includes/tapir'); global $conf; $conf['i18n_variables'][] = 'uc_store_name'; } /** * Implements hook_element_info(). */ function uc_store_element_info() { $types = array(); $types['tapir_table'] = array( '#columns' => array(), '#rows' => array(), '#tree' => TRUE, '#value' => NULL, '#pre_render' => array('tapir_gather_rows'), '#theme' => 'tapir_table', '#process' => array('ajax_process_form'), ); $types['uc_address'] = array( '#input' => TRUE, '#required' => TRUE, '#process' => array('uc_store_process_address_field'), '#attributes' => array('class' => array('uc-store-address-field')), '#theme_wrappers' => array('container'), '#key_prefix' => '', '#hidden' => FALSE, ); $sign_flag = variable_get('uc_sign_after_amount', FALSE); $currency_sign = variable_get('uc_currency_sign', '$'); $types['uc_price'] = array( '#input' => TRUE, '#size' => 15, '#maxlength' => 15, '#autocomplete_path' => FALSE, '#process' => array('ajax_process_form'), '#element_validate' => array('uc_store_validate_number'), '#theme' => 'textfield', '#theme_wrappers' => array('form_element'), '#field_prefix' => $sign_flag ? '' : $currency_sign, '#field_suffix' => $sign_flag ? $currency_sign : '', '#allow_negative' => FALSE, '#empty_zero' => TRUE, ); $types['uc_quantity'] = array( '#input' => TRUE, '#size' => 5, '#maxlength' => 6, '#required' => TRUE, '#autocomplete_path' => FALSE, '#process' => array('ajax_process_form'), '#element_validate' => array('uc_store_validate_uc_quantity'), '#theme' => 'textfield', '#theme_wrappers' => array('form_element'), '#allow_zero' => FALSE, ); return $types; } /** * Element process hook for address fields. */ function uc_store_process_address_field($element, $form_state) { $element['#tree'] = TRUE; $prefix = $element['#key_prefix'] ? ($element['#key_prefix'] . '_') : ''; $weight = uc_store_address_field_weights(); if (isset($form_state['uc_address'])) { // Use submitted Ajax values. $value = $form_state['uc_address']; } elseif (is_array($element['#value']) || is_object($element['#value'])) { // Use provided default value. $value = (array) $element['#value']; } else { $value = array(); } $countries = db_query("SELECT country_id, country_name FROM {uc_countries} WHERE version > :version", array(':version' => 0))->fetchAllKeyed(); foreach ($countries as $country_id => $country_name) { $countries[$country_id] = t($country_name); } natcasesort($countries); // Force the selected country to a valid one, so the zone dropdown matches. if (isset($value[$prefix . 'country']) && !isset($countries[$value[$prefix . 'country']])) { $country_keys = array_keys($countries); $value[$prefix . 'country'] = $country_keys[0]; } // Iterating on the UcAddress object excludes non-public properties, which // is exactly what we want to do. $address = new UcAddress(); foreach ($address as $base_field => $field_value) { $field = $prefix . $base_field; if (!isset($value[$field])) { continue; } switch ($base_field) { case 'country': $subelement = array( '#type' => 'select', '#options' => $countries, '#ajax' => array( 'callback' => 'uc_store_update_address_field_zones', 'wrapper' => 'uc-store-address-' . str_replace('_', '-', $prefix) . 'zone-wrapper', 'progress' => array( 'type' => 'throbber', ), ), '#element_validate' => array('uc_store_validate_address_field_country'), '#key_prefix' => $element['#key_prefix'], ); break; case 'zone': $subelement = array( '#prefix' => '
', '#suffix' => '
', ); $zones = db_query("SELECT zone_id, zone_name FROM {uc_zones} WHERE zone_country_id = :country", array(':country' => $value[$prefix . 'country']))->fetchAllKeyed(); if (!empty($zones)) { natcasesort($zones); $subelement += array( '#type' => 'select', '#options' => $zones, '#empty_value' => 0, ); } else { $subelement += array( '#type' => 'hidden', '#value' => 0, '#required' => FALSE, ); } break; case 'postal_code': $subelement = array( '#type' => 'textfield', '#size' => 10, '#maxlength' => 10, ); break; case 'phone': $subelement = array( '#type' => 'textfield', '#size' => 16, '#maxlength' => 32, ); break; default: $subelement = array( '#type' => 'textfield', '#size' => 32, ); } // Copy JavaScript states from the parent element. if (isset($element['#states'])) { $subelement['#states'] = $element['#states']; } // Set common values for all address fields. $label = uc_get_field_name($base_field); $element[$field] = $subelement + array( '#title' => $label ? $label : ' ', '#default_value' => $value[$field], '#parents' => array_merge(array_slice($element['#parents'], 0, -1), array($field)), '#pre_render' => array('uc_store_pre_render_address_field'), '#access' => $element['#hidden'] ? FALSE : uc_address_field_enabled($base_field), '#required' => $element['#required'] ? uc_address_field_required($base_field) : FALSE, '#weight' => (isset($weight[$base_field])) ? $weight[$base_field] : 0, ); } return $element; } /** * Element validation callback for country field. * * Store the current address for use when rebuilding the form. */ function uc_store_validate_address_field_country($element, &$form_state) { $address = drupal_array_get_nested_value($form_state['values'], array_slice($element['#parents'], 0, -1)); $form_state['uc_address'] = isset($form_state['uc_address']) ? array_merge($form_state['uc_address'], $address) : $address; } /** * Ajax callback: updates the zone select box when the country is changed. */ function uc_store_update_address_field_zones($form, &$form_state) { $element = &$form; foreach (array_slice($form_state['triggering_element']['#array_parents'], 0, -1) as $field) { $element = &$element[$field]; } $prefix = empty($element['#key_prefix']) ? '' : ($element['#key_prefix'] . '_'); return $element[$prefix . 'zone']; } /** * Prerenders address field elements to move the required marker when needed. */ function uc_store_pre_render_address_field($element) { if (!empty($element['#required'])) { $element['#title'] = theme('form_required_marker', $element) . ' ' . $element['#title']; unset($element['#required']); } return $element; } /** * Helper function to determine the value for a uc_price form element. */ function form_type_uc_price_value($element, $input = FALSE) { if ($input === FALSE && !empty($element['#default_value'])) { return uc_store_format_price_field_value($element['#default_value']); } elseif (empty($input) && empty($element['#required']) && !empty($element['#empty_zero'])) { // Empty non-required prices should be treated as zero. return 0; } } /** * Generic form element validation handler for numbers. */ function uc_store_validate_number(&$element, &$form_state) { $value = $element['#value']; if ($value != '') { if (!is_numeric($value)) { form_error($element, t('%name must be a number.', array('%name' => $element['#title']))); } elseif (empty($element['#allow_negative']) && $value < 0) { form_error($element, t('%name must not be negative.', array('%name' => $element['#title']))); } } } /** * Form element validation handler for #type 'uc_quantity'. */ function uc_store_validate_uc_quantity(&$element, &$form_state) { if (!preg_match('/^\d+$/', $element['#value'])) { form_error($element, t('The quantity must be a number.')); } elseif (empty($element['#allow_zero']) && !$element['#value']) { form_error($element, t('The quantity cannot be zero.')); } } /** * Implements hook_theme(). */ function uc_store_theme() { return array( 'uc_store_footer' => array( 'variables' => array('message' => ''), 'file' => 'uc_store.theme.inc', ), 'uc_store_address_fields_form' => array( 'render element' => 'form', 'file' => 'uc_store.admin.inc', ), 'uc_pane_sort_table' => array( 'render element' => 'form', 'file' => 'uc_store.theme.inc', ), 'tapir_table' => array( 'render element' => 'element', ), 'uc_price' => array( 'variables' => array('price' => 0, 'suffixes' => array()), 'file' => 'uc_store.theme.inc', ), 'uc_qty_label' => array( 'variables' => array(), 'file' => 'uc_store.theme.inc', ), 'uc_qty' => array( 'variables' => array('qty' => 1), 'file' => 'uc_store.theme.inc', ), 'uc_uid' => array( 'variables' => array('uid' => 0), 'file' => 'uc_store.theme.inc', ), ); } /** * Implements hook_help(). */ function uc_store_help($path, $arg) { switch ($path) { case 'admin/help#uc_store': $output = '

' . t('Ubercart') . '

'; $output .= '

' . t('Use the following links to find documentation and support:') . '

'; $output .= ''; return $output; case 'admin/store/reports': $output = '

' . t('Various reports generated by Ubercart modules can be found here. Click the links below to view the reports.') . '

'; if (!module_exists('uc_reports')) { $output .= '

' . t('To view core Ubercart statistics enable the Reports module on the module administration page.', array('!url' => url('admin/modules', array('fragment' => 'edit-modules-ubercart-core-optional')))) . '

'; } return $output; } } /** * Implements hook_permission(). */ function uc_store_permission() { return array( 'administer store' => array( 'title' => t('Administer store'), 'restrict access' => TRUE, ), 'view reports' => array( 'title' => t('View reports'), ), ); } /** * Implements hook_date_formats(). */ function uc_store_date_formats() { return array( array( 'type' => 'uc_store', 'format' => 'Y-m-d', 'locales' => array(), ), array( 'type' => 'uc_store', 'format' => 'm/d/Y', 'locales' => array('en-us'), ), array( 'type' => 'uc_store', 'format' => 'd/m/Y', 'locales' => array('en-gb', 'en-hk', 'en-ie', 'el-gr', 'es-es', 'fr-be', 'fr-fr', 'fr-lu', 'it-it', 'nl-be', 'pt-pt'), ), array( 'type' => 'uc_store', 'format' => 'Y/m/d', 'locales' => array('en-ca', 'fr-ca', 'no-no', 'sv-se'), ), array( 'type' => 'uc_store', 'format' => 'd.m.Y', 'locales' => array('de-ch', 'de-de', 'de-lu', 'fi-fi', 'fr-ch', 'is-is', 'pl-pl', 'ro-ro', 'ru-ru'), ), ); } /** * Implements hook_date_format_types(). */ function uc_store_date_format_types() { return array('uc_store' => t('Ubercart')); } /** * Implements hook_page_alter(). */ function uc_store_page_alter(&$page) { $id = variable_get('uc_footer_message', 0); // Exit if the store footer is turned off. if ($id === 'none') { return; } // Figure out what page is being viewed. $path = drupal_get_normal_path($_GET['q']); $parts = explode('/', $path); // Exit if the page isn't governed by Ubercart. switch ($parts[0]) { case 'admin': // No footer on /admin or /admin/*. // But add a footer on /admin/store and /admin/store/*. if (!isset($parts[1]) || $parts[1] != 'store') { return; } break; case 'node': // No footer on /node or /node/[type]/add. // Only add a footer on /node/[nid] if that node is a product. if (count($parts) != 2 || intval($parts[1]) == 0) { return; } else { $node = node_load($parts[1]); if ($node == FALSE || !function_exists('uc_product_node_info') || !uc_product_is_product($node->type)) { return; } } break; case 'catalog': case 'cart': break; default: return; } $messages = _uc_store_footer_options(); if ($id == 0) { // Pseudorandom number based on the hash of the path and the site's private // key, so messages are consistent between pages on the same site, but // different on the same pages on different sites. $id = (hexdec(substr(md5($path . drupal_get_private_key()), 0, 2)) % count($messages)) + 1; } $page['page_bottom']['ubercart_footer'] = array( '#theme' => 'uc_store_footer', '#message' => $messages[$id], ); } /** * Implements hook_reviews(). * * Provides code reviews for coder_review.module. */ function uc_store_reviews() { $coder_reviews = array(); $path = drupal_get_path('module', 'uc_store') . '/includes'; $files = drupal_system_listing('/coder_review_.*\.inc$/', $path, 'filepath', 0); foreach ($files as $file) { require_once DRUPAL_ROOT . '/' . $file->uri; $function = $file->name . '_reviews'; if (function_exists($function)) { if ($review = call_user_func($function)) { $coder_reviews = array_merge($coder_reviews, $review); } } } return $coder_reviews; } /** * Returns the default store footer options. */ function _uc_store_footer_options() { $url = array('!url' => 'http://www.ubercart.org/'); return array( 1 => t('Powered by Ubercart', $url), 2 => t('Drupal e-commerce provided by Ubercart.', $url), 3 => t('Supported by Ubercart, an open source e-commerce suite.', $url), 4 => t('Powered by Ubercart, the free shopping cart software.', $url), ); } /** * Helper function for hook_entity_property_info() and hook_rules_data_info(). * * Should be used by implementations of those hooks that wish to wrap address * selectors. */ function uc_address_property_info() { return array( 'first_name' => array( 'type' => 'text', 'label' => t('First name'), 'description' => t('First name of the addressee.'), ), 'last_name' => array( 'type' => 'text', 'label' => t('Last name'), 'description' => t('Last name of the addressee.'), ), 'company' => array( 'type' => 'text', 'label' => t('Company'), 'description' => t('Name of the company at the address.'), ), 'street1' => array( 'type' => 'text', 'label' => t('Street line 1'), 'description' => t('First line of the street address.'), ), 'street2' => array( 'type' => 'text', 'label' => t('Street line 2'), 'description' => t('Second line of the street address.'), ), 'city' => array( 'type' => 'text', 'label' => t('City'), 'description' => t('Address city.'), ), 'zone' => array( 'type' => 'integer', 'label' => t('Zone'), 'description' => t('Address state/province/zone.'), 'options list' => 'uc_zone_option_list', ), 'postal_code' => array( 'type' => 'text', 'label' => t('Postal code'), 'description' => t('Address post code.'), ), 'country' => array( 'type' => 'integer', 'label' => t('Country'), 'description' => t('Address country.'), 'options list' => 'uc_country_option_list', ), 'phone' => array( 'type' => 'text', 'label' => t('Phone'), 'description' => t('Contact phone number.'), ), 'email' => array( 'type' => 'text', 'label' => t('Email'), 'description' => t('Contact email address.'), ), ); } /** * Returns an IMG tag for a store icon. Deprecated; use theme('image') instead. * * @param $path * The Drupal path of the menu item. Atlernately may specify a filename by * passing this string as file:filename.png. * @param $small * Pass TRUE to get a link to the small version of the icon. If specifying a * filename, you should let this be FALSE. * * @return * HTML output for the image. */ function uc_store_get_icon($path, $small = FALSE, $class = 'uc-store-icon', $alt = NULL) { $file = FALSE; switch ($path) { case 'admin/store': $file = 'store_monitor'; break; case 'admin/store/orders': $file = 'menu_orders'; break; case 'admin/store/customers': $file = 'menu_customers'; break; case 'admin/store/products': $file = 'menu_products'; break; case 'admin/store/reports': $file = 'menu_reports'; break; case 'admin/store/settings': $file = 'menu_store_settings'; break; case 'admin/store/help': $file = 'menu_help'; break; } if (substr($path, 0, 5) == 'file:') { $file = substr($path, 5); } if (!$file) { // See if it's hooked in anywhere else... return ''; } if ($small) { $file .= '_small'; } return theme('image', array( 'path' => drupal_get_path('module', 'uc_store') . '/images/' . $file . '.gif', 'alt' => $alt, 'attributes' => array('class' => array($class)), )); } /** * Formats an amount for display with the store's currency settings. * * @param $value * The numeric value of the currency amount. * @param $sign * The currency symbol. If FALSE is given, no symbol is used. The default, * NULL, causes the variable 'uc_currency_sign' to be used, which defaults to * '$'. * @param $thou * The thousands separator character. If FALSE is given, no separator is used. * The default, NULL, causes the variable 'uc_currency_sign' to be used, which * defaults to ','. * @param $dec * The decimal separator character. If FALSE is given, confusion will abound, * because it will look 100 times bigger. The default, NULL, causes the * variable 'uc_currency_dec' to be used, which defaults to '.'. * * @return * String containing price formatted with currency symbol and separators. */ function uc_currency_format($value, $sign = NULL, $thou = NULL, $dec = NULL) { if ($value === NULL) { return NULL; } $output = ''; $sign_after = variable_get('uc_sign_after_amount', FALSE); $prec = variable_get('uc_currency_prec', 2); if (is_null($sign)) { $sign = variable_get('uc_currency_sign', '$'); } if (is_null($thou)) { $thou = variable_get('uc_currency_thou', ','); } if (is_null($dec)) { $dec = variable_get('uc_currency_dec', '.'); } // If the value is significantly less than the minimum precision, zero it. if ($prec > 0 && round(abs($value), $prec + 1) < pow(10, -$prec)) { $value = 0; } // Force the price to a positive value and add a negative sign if necessary. if ($value < 0) { $value = abs($value); $output .= '-'; } // Add the currency sign first if specified. if ($sign && !$sign_after) { $output .= $sign; } // Format the number, like 1234.567 => 1,234.57 $output .= number_format($value, $prec, $dec, $thou); // Add the currency sign last if specified. if ($sign && $sign_after) { $output .= $sign; } return $output; } /** * Formats a weight value for display. * * @param $value * Numerical weight value. * @param $unit * Weight unit. One of 'lb', 'oz', 'kg', or 'g', or NULL to use store * default weight units. * * @return * String containing formattted weight, including weight units. */ function uc_weight_format($value, $unit = NULL) { $vars = array('!value' => $value); if (is_null($unit)) { $unit = variable_get('uc_weight_unit', 'lb'); } $defaults = array( 'lb' => '!value lb.', 'oz' => '!value oz.', 'kg' => '!valuekg', 'g' => '!valueg', ); $pattern = variable_get('uc_weight_format_' . $unit, $defaults[$unit]); if (strpos($pattern, '!value') === FALSE) { $pattern = $defaults[$unit]; } $format = strtr($pattern, $vars); return $format; } /** * Gets the conversion ratio from one unit of weight to another. */ function uc_weight_conversion($from_units, $to_units = NULL) { if (is_null($to_units)) { $to_units = variable_get('uc_weight_unit', 'lb'); } $constant = strtoupper($from_units) . '_TO_' . strtoupper($to_units); if (defined($constant) && ($conversion = constant($constant)) > 0) { return $conversion; } else { return 1; } } /** * Formats a length value for display. * * @param $value * Numerical length value. * @param $unit * Length unit. One of 'ft', 'in', 'cm', or 'mm', or NULL to use store * default length units. * * @return * String containing formattted length, including length units. */ function uc_length_format($value, $unit = NULL) { $vars = array('!value' => $value); if (is_null($unit)) { $unit = variable_get('uc_length_unit', 'in'); } $defaults = array( 'in' => '!valuein.', 'ft' => '!valueft.', 'cm' => '!valuecm', 'mm' => '!valuemm', ); $pattern = variable_get('uc_length_format_' . $unit, $defaults[$unit]); if (strpos($pattern, '!value') === FALSE) { $pattern = $defaults[$unit]; } $format = strtr($pattern, $vars); return $format; } /** * Gets the conversion ratio from one unit of length to another. */ function uc_length_conversion($from_units, $to_units = NULL) { if (is_null($to_units)) { $to_units = variable_get('uc_length_unit', 'in'); } $constant = strtoupper($from_units) . '_TO_' . strtoupper($to_units); if (defined($constant) && ($conversion = constant($constant)) > 0) { return $conversion; } else { return 1; } } /** * Formats a date value for display. * * @param $month * Numerical month value. * @param $day * Numerical day value. * @param $year * Numerical year value. * * @return * String containing formattted date, using the 'uc_store' date format. */ function uc_date_format($month, $day, $year) { $time = strtotime($month . '/' . $day . '/' . $year); return format_date($time, 'uc_store'); } /** * Saves the address format for a country. */ function uc_set_address_format($country_id, $format) { variable_set('uc_address_format_' . intval($country_id), $format); } /** * Formats an address for display based on a country's address format. */ function uc_address_format($first_name, $last_name, $company, $street1, $street2, $city, $zone, $postal_code, $country) { $result = db_query("SELECT * FROM {uc_zones} WHERE zone_id = :id", array(':id' => $zone)); if (!($zone_data = $result->fetchAssoc())) { $zone_data = array('zone_code' => t('N/A'), 'zone_name' => t('Unknown')); } $result = db_query("SELECT * FROM {uc_countries} WHERE country_id = :id", array(':id' => $country)); if (!($country_data = $result->fetchAssoc())) { $country_data = array( 'country_name' => t('Unknown'), 'country_iso_code_2' => t('N/A'), 'country_iso_code_3' => t('N/A'), ); } $variables = array( "\r\n" => '
', '!company' => check_plain($company), '!first_name' => check_plain($first_name), '!last_name' => check_plain($last_name), '!street1' => check_plain($street1), '!street2' => check_plain($street2), '!city' => check_plain($city), '!zone_code' => $zone_data['zone_code'], '!zone_name' => $zone_data['zone_name'], '!postal_code' => check_plain($postal_code), '!country_name' => t($country_data['country_name']), '!country_code2' => $country_data['country_iso_code_2'], '!country_code3' => $country_data['country_iso_code_3'], ); if (uc_store_default_country() != $country) { $variables['!country_name_if'] = t($country_data['country_name']); $variables['!country_code2_if'] = $country_data['country_iso_code_2']; $variables['!country_code3_if'] = $country_data['country_iso_code_3']; } else { $variables['!country_name_if'] = ''; $variables['!country_code2_if'] = ''; $variables['!country_code3_if'] = ''; } $format = variable_get('uc_address_format_' . $country, ''); if (empty($format)) { $format = "!company\r\n!first_name !last_name\r\n!street1\r\n!street2\r\n!city, !zone_code !postal_code\r\n!country_name_if"; } $address = strtr($format, $variables); $address = strtr($address, array("\n" => '
')); $match = array('`^`', '`$`', '`(\s*|[\s*\s*]+)`', '``', '`, N/A`'); $replace = array('', '', '
', '
', '', ''); $address = preg_replace($match, $replace, $address); return $address; } /** * Returns the code abbreviation for a zone based on the zone ID or name. */ function uc_get_zone_code($zone = NULL) { if (empty($zone)) { return FALSE; } if (is_numeric($zone)) { $result = db_query("SELECT zone_code FROM {uc_zones} WHERE zone_id = :id", array(':id' => $zone)); } else { $result = db_query("SELECT zone_code FROM {uc_zones} WHERE zone_name = :name", array(':name' => $zone)); } if ($row = $result->fetchObject()) { return $row->zone_code; } return FALSE; } /** * Returns country data based on the supplied criteria. * * @param $match * An associative array of fields to match. * @param $sort * The field to sort by. */ function uc_get_country_data($match = array(), $sort = 'country_name') { $valid_fields = array('country_id', 'country_name', 'country_iso_code_2', 'country_iso_code_3', 'version'); if (!is_array($match)) { $match = array(); } if (!in_array($sort, $valid_fields)) { $sort = 'country_name'; } $query = db_select('uc_countries') ->fields('uc_countries') ->orderBy($sort); if (count($match) > 0) { $where = ''; foreach ($match as $key => $value) { if (!in_array($key, $valid_fields)) { continue; } $query->condition($key, $value); } } $countries = $query->execute()->fetchAll(PDO::FETCH_ASSOC); return empty($countries) ? FALSE : $countries; } /** * Returns the name of an address field. */ function uc_get_field_name($field) { $fields = array( 'first_name' => t('First name'), 'last_name' => t('Last name'), 'company' => t('Company'), 'street1' => t('Street address'), 'street2' => t(''), 'city' => t('City'), 'zone' => t('State/Province'), 'country' => t('Country'), 'postal_code' => t('Postal code'), 'phone' => t('Phone number'), 'email' => t('E-mail'), ); if (!isset($fields[$field])) { drupal_set_message(t('The field title %field is being accessed incorrectly.', array('%field' => $field)), 'error'); return ''; } return variable_get('uc_field_' . $field, $fields[$field]); } /** * Returns TRUE if the address field is enabled. */ function uc_address_field_enabled($field = NULL) { $fields = variable_get('uc_address_fields', drupal_map_assoc(array('first_name', 'last_name', 'phone', 'company', 'street1', 'street2', 'city', 'zone', 'postal_code', 'country'))); return $field ? isset($fields[$field]) : $fields; } /** * Returns TRUE if the address field is required. */ function uc_address_field_required($field) { $fields = variable_get('uc_address_fields_required', drupal_map_assoc(array('first_name', 'last_name', 'street1', 'city', 'zone', 'postal_code', 'country'))); return isset($fields[$field]); } /** * Returns the weights of address fields. */ function uc_store_address_field_weights() { return variable_get('uc_address_fields_weight', array('first_name' => 0, 'last_name' => 1, 'company' => 2, 'street1' => 3, 'street2' => 4, 'city' => 5, 'zone' => 6, 'country' => 7, 'postal_code' => 8, 'phone' => 9)); } /** * A simple Forms API textfield generator... */ function uc_textfield($title, $default = NULL, $required = TRUE, $description = NULL, $maxlength = 32, $size = 32) { if (is_null($title) || empty($title)) return NULL; $textfield = array( '#type' => 'textfield', '#title' => $title, '#description' => $description, '#size' => $size, '#maxlength' => $maxlength, '#required' => $required, '#default_value' => $default, ); return $textfield; } /** * Retrieves a zone's name from the database, using its ID. * * @param $id * The zone's ID. */ function uc_zone_get_by_id($id) { return db_query("SELECT zone_name FROM {uc_zones} WHERE zone_id = :id", array(':id' => $id))->fetchField(); } /** * Creates a zone select box for a form. * * @param $title * The label for the field. * @param $default * The default zone ID. * @param $country_id * The country ID * @param array $options * An associative array of additional options, with the following elements: * - 'description': The description for the field (defaults to none). * - 'display': The values to display, either 'name' (default) or 'code'. * - 'required': TRUE if the field is required (defaults to FALSE). * * @return * A Form API select element. */ function uc_zone_select($title = '', $default = NULL, $country_id = NULL, $options = array()) { $options += array( 'description' => NULL, 'display' => 'name', 'required' => FALSE, ); if (empty($country_id)) { $country_id = uc_store_default_country(); } $order_by = ($options['display'] == 'code') ? 'zone_code' : 'zone_name'; $result = db_query('SELECT * FROM {uc_zones} WHERE zone_country_id = :id ORDER BY :field', array(':id' => $country_id, ':field' => $order_by)); $zones = array('' => t('Please select')); foreach ($result as $zone) { $zones[$zone->zone_id] = $zone->$order_by; } if (count($zones) == 1) { $zones = array(-1 => t('Not applicable')); } $select = array( '#type' => 'select', '#title' => $title, '#description' => $options['description'], '#options' => $zones, '#default_value' => $default, '#required' => $options['required'], '#disabled' => isset($zones[-1]), ); return $select; } /** * Helper function to return zone options, grouped by country. */ function uc_zone_option_list() { $result = db_query("SELECT z.*, c.country_name FROM {uc_zones} z LEFT JOIN {uc_countries} c ON z.zone_country_id = c.country_id ORDER BY c.country_name, z.zone_name"); foreach ($result as $zone) { $options[t($zone->country_name)][$zone->zone_id] = $zone->zone_name; } uksort($options, 'strnatcasecmp'); return $options; } /** * Retrieves a country's name from the database, using its ID. * * @param $id * The country's ISO 3166-1 numeric identifier. */ function uc_country_get_by_id($id) { return db_query("SELECT country_name FROM {uc_countries} WHERE country_id = :id", array(':id' => $id))->fetchField(); } /** * Returns a list of available countries. */ function uc_country_option_list() { $result = db_query("SELECT * FROM {uc_countries} WHERE version > :version", array(':version' => 0)); $options = array(); while ($country = $result->fetchAssoc()) { $options[$country['country_id']] = t($country['country_name']); } if (count($options) == 0) { $options[] = t('No countries found.'); } natcasesort($options); return $options; } /** * Creates a day select box for a form. */ function uc_select_day($title = NULL, $default = NULL, $allow_empty = FALSE) { $options = $allow_empty ? array('' => '') : array(); $select = array( '#type' => 'select', '#title' => (is_null($title) ? t('Day') : $title), '#options' => $options + drupal_map_assoc(range(1, 31)), '#default_value' => (is_null($default) ? 0 : $default), ); return $select; } /** * Creates a month select box for a form. */ function uc_select_month($title = NULL, $default = NULL, $allow_empty = FALSE) { $options = $allow_empty ? array('' => '') : array(); $select = array( '#type' => 'select', '#title' => (is_null($title) ? t('Month') : $title), '#options' => $options + array(1 => t('01 - January'), 2 => t('02 - February'), 3 => t('03 - March'), 4 => t('04 - April'), 5 => t('05 - May'), 6 => t('06 - June'), 7 => t('07 - July'), 8 => t('08 - August'), 9 => t('09 - September'), 10 => t('10 - October'), 11 => t('11 - November'), 12 => t('12 - December') ), '#default_value' => (is_null($default) ? 0 : $default), ); return $select; } /** * Creates a year select box for a form. */ function uc_select_year($title = NULL, $default = NULL, $min = NULL, $max = NULL, $allow_empty = FALSE) { $min = is_null($min) ? intval(date('Y')) : $min; $max = is_null($max) ? intval(date('Y')) + 20 : $max; $options = $allow_empty ? array('' => '') : array(); $select = array( '#type' => 'select', '#title' => (is_null($title) ? t('Year') : $title), '#options' => $options + drupal_map_assoc(range($min, $max)), '#default_value' => (is_null($default) ? 0 : $default), ); return $select; } /** * Creates an address select box based on a user's previous orders. * * @param $uid * The user's ID to search for in the orders table. * @param $type * Choose either 'shipping' or 'billing'. */ function uc_select_address($uid, $type = 'billing', $onchange = '', $title = NULL) { $addresses = uc_get_addresses($uid, $type); if (!is_array($addresses) || count($addresses) == 0) { return NULL; } $options = array('0' => t('Select one...')); foreach ($addresses as $key => $address) { $option = $address['street1']; // Check if the address is a duplicate (i.e. same address, but sent to // different person). if ((isset($addresses[$key - 1]) && $option == $addresses[$key - 1]['street1']) || (isset($addresses[$key + 1]) && $option == $addresses[$key + 1]['street1'])) { $option .= ' - ' . $address['first_name'] . ' ' . $address['last_name']; } $options[drupal_json_encode($address)] = check_plain($option); } $select = array( '#type' => 'select', '#title' => is_null($title) ? t('Address book') : $title, '#options' => $options, '#attributes' => array('onchange' => $onchange), ); return $select; } /** * Creates an address select box based on a user's previous orders. * * @param $uid * The user's ID to search for in the orders table. * @param $type * Choose either 'shipping' or 'billing'. */ function uc_select_addresses($uid, $type = 'billing') { $addresses = uc_get_addresses($uid, $type); if (empty($addresses)) { return array(); } $options = array(-1 => t('Select one...')); foreach ($addresses as $key => $address) { $option = $address['street1']; // Check if the address is a duplicate (i.e. same address, but sent to // different person). if ((isset($addresses[$key - 1]) && $option == $addresses[$key - 1]['street1']) || (isset($addresses[$key + 1]) && $option == $addresses[$key + 1]['street1'])) { $option .= ' - ' . $address['first_name'] . ' ' . $address['last_name']; } $options[$key] = check_plain($option); } $addresses['#options'] = $options; return $addresses; } /** * Loads a customer's previously given addresses. */ function uc_get_addresses($uid, $type = 'billing') { if ($uid == 0) { return NULL; } if ($type == 'delivery') { $type = 'delivery'; } else { $type = 'billing'; } $query = db_select('uc_orders', 'o')->distinct(); $alias = array(); $alias['first_name'] = $query->addField('o', $type . '_first_name', 'first_name'); $alias['last_name'] = $query->addField('o', $type . '_last_name', 'last_name'); $alias['phone'] = $query->addField('o', $type . '_phone', 'phone'); $alias['company'] = $query->addField('o', $type . '_company', 'company'); $alias['street1'] = $query->addField('o', $type . '_street1', 'street1'); $alias['street2'] = $query->addField('o', $type . '_street2', 'street2'); $alias['city'] = $query->addField('o', $type . '_city', 'city'); $alias['zone'] = $query->addField('o', $type . '_zone', 'zone'); $alias['postal_code'] = $query->addField('o', $type . '_postal_code', 'postal_code'); $alias['country'] = $query->addField('o', $type . '_country', 'country'); // In pgsql, ORDER BY requires the field being sorted by to be in the SELECT // list. But if we have the 'created' column in the SELECT list, the DISTINCT // is rather useless. So we will just sort addresses alphabetically. $query->condition('uid', $uid) ->condition('order_status', uc_order_status_list('general', TRUE), 'IN') ->orderBy($alias['street1']); $result = $query->execute(); $addresses = array(); while ($address = $result->fetchAssoc()) { if (!empty($address['street1']) || !empty($address['postal_code'])) { $addresses[] = $address; } } return $addresses; } /** * Returns an array of country files that can be installed or updated. */ function _uc_country_import_list() { $dir = drupal_get_path('module', 'uc_store') . '/countries/'; $countries = array(); if (is_dir($dir)) { if ($dh = opendir($dir)) { while (($file = readdir($dh)) !== FALSE) { switch (filetype($dir . $file)) { case 'file': if (substr($file, -4, 4) == '.cif') { $pieces = explode('_', substr($file, 0, strlen($file) - 4)); $country_id = intval($pieces[count($pieces) - 2]); $version = $pieces[count($pieces) - 1]; if (!isset($countries[$country_id])) { $countries[$country_id]['version'] = $version; $countries[$country_id]['file'] = $file; } else { if ($version > $countries[$country_id]['version']) { $countries[$country_id]['version'] = $version; $countries[$country_id]['file'] = $file; } } } break; } } closedir($dh); } } return $countries; } /** * Sorts an array of arrays having a weight key to determine their order. */ function uc_weight_sort($a, $b) { if ($a['weight'] == $b['weight']) { return 0; } return ($a['weight'] > $b['weight']) ? 1 : -1; } /** * Returns the default message for a configurable message. */ function uc_get_message($message_id) { static $messages; if (empty($messages)) { $messages = module_invoke_all('uc_message'); drupal_alter('uc_get_message', $messages); } return $messages[$message_id]; } /** * Returns the store name if set, or the site name otherwise. */ function uc_store_name() { return variable_get('uc_store_name', variable_get('site_name', 'Ubercart')); } /** * Returns the user-defined store address. */ function uc_store_address() { $store_address = uc_address_format(NULL, NULL, uc_store_name(), variable_get('uc_store_street1', NULL), variable_get('uc_store_street2', NULL), variable_get('uc_store_city', NULL), variable_get('uc_store_zone', NULL), variable_get('uc_store_postal_code', NULL), uc_store_default_country()); return $store_address; } /** * Returns the store e-mail address if set, otherwise the site email address. */ function uc_store_email() { $email_from = variable_get('uc_store_email', ''); if (empty($email_from)) { $email_from = variable_get('site_mail', ini_get('sendmail_from')); } return $email_from; } /** * Returns store name and e-mail address in an RFC 2822 compliant string. * * The return string is intended for use as a "From" address when sending * e-mail to customers. It will look something like: * Store Name * * @return * An RFC 2822 compliant e-mail address. */ function uc_store_email_from() { $email_from = uc_store_email(); // Add the store name to the e-mail "From" line. // Must be optional to prevent server conflicts. if (variable_get('uc_store_email_include_name', TRUE)) { $email_from = uc_store_rfc2822_display_name(uc_store_name()) . ' <' . $email_from . '>'; } return $email_from; } /** * Turns a text string into a valid RFC 2822 quoted string. * * Any text string not consisting of a limited set of valid characters * (notable printable non-valid characters include ',' and '.') needs * to be quoted in order to be used an an e-mail header such as the "From" * address. Double quotes in the original string are escaped (and nothing else). * * @param $name * The text string to convert to a RFC 2822 quoted string. */ function uc_store_rfc2822_display_name($name) { // Base64 encode $name string if it contains non-ASCII characters. $name = mime_header_encode($name); // From RFC2822, section 3.4.2, define valid characters for an atom. $valid_chars = "[a-zA-Z0-9\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~]"; // Display name is composed of 0 or more atoms separated by white space. if (!preg_match("/^(${valid_chars}*[ \t]*)*$/", $name)) { return '"' . addcslashes($name, '"') . '"'; } return $name; } /** * Derives a valid username from an e-mail address. * * @param $email * An e-mail address. * * @return * A username derived from the e-mail address, using the part of the address * up to the @ with integers appended to the end if needed to avoid a * duplicate username. */ function uc_store_email_to_username($email) { // Default to the first part of the e-mail address. $name = substr($email, 0, strpos($email, '@')); // Remove possible illegal characters. $name = preg_replace('/[^A-Za-z0-9_.-]/', '', $name); // Trim that value for spaces and length. $name = trim(substr($name, 0, USERNAME_MAX_LENGTH - 4)); // Make sure we don't hand out a duplicate username. while (db_query("SELECT COUNT(uid) FROM {users} WHERE name LIKE :name", array(':name' => $name))->fetchField() > 0) { // If the username got too long, trim it back down. if (strlen($name) == USERNAME_MAX_LENGTH) { $name = substr($name, 0, USERNAME_MAX_LENGTH - 4); } // Append a random integer to the name. $name .= rand(0, 9); } return $name; } /** * Logs encryption errors to watchdog. * * @param $crypt * The object used to perform your encryption/decryption. * @param $module * The module name to specify in the watchdog notices. */ function uc_store_encryption_errors(&$crypt, $module) { $errors = $crypt->getErrors(); if (!empty($errors)) { foreach ($errors as $message) { $items[] = $message; } watchdog('encryption', 'Encryption failed. !messages', array('!messages' => theme('item_list', array('items' => $items))), WATCHDOG_ERROR); } } /** * Returns a default store country value. */ function uc_store_default_country() { static $default; if (!empty($default)) { return $default; } $default = variable_get('uc_store_country', 840); $result = db_query("SELECT COUNT(*) FROM {uc_countries} WHERE country_id = :id AND version > :version", array(':id' => $default, ':version' => 0))->fetchField(); if ($result == 0) { $default = db_query_range("SELECT country_id FROM {uc_countries} WHERE version > :version", 0, 1, array(':version' => 0))->fetchField(); } return $default; } /** * Gets image widgets defined by various modules. */ function uc_store_get_image_widgets() { return module_invoke_all('uc_image_widget'); } /** * Implements hook_uc_image_widget(). * * Built-in support for Colorbox, Thickbox and Lightbox2. */ function uc_store_uc_image_widget() { $widgets = array(); if (module_exists('colorbox')) { $widgets['colorbox'] = array( 'name' => t('Colorbox'), 'callback' => 'uc_store_image_widget_colorbox', ); } if (module_exists('thickbox')) { $widgets['thickbox'] = array( 'name' => t('Thickbox'), 'callback' => 'uc_store_image_widget_thickbox', ); } if (module_exists('lightbox2')) { $widgets['lightbox2'] = array( 'name' => t('Lightbox2'), 'callback' => 'uc_store_image_widget_lightbox2', ); } return $widgets; } /** * Generates the Colorbox-specific HTML attributes. */ function uc_store_image_widget_colorbox($rel_count) { if (!is_null($rel_count)) { $img_index = 'uc_image_' . $rel_count; } else { $img_index = 'uc_image'; } return ' class="colorbox" rel="' . $img_index . '"'; } /** * Generates the Thickbox-specific HTML attributes. */ function uc_store_image_widget_thickbox($rel_count) { if (!is_null($rel_count)) { $img_index = 'uc_image_' . $rel_count; } else { $img_index = 'uc_image'; } return ' class="thickbox" rel="' . $img_index . '"'; } /** * Generates the Lightbox2-specific HTML attributes. */ function uc_store_image_widget_lightbox2($rel_count) { if (!is_null($rel_count)) { $img_index = 'lightbox[' . $rel_count . ']'; } else { $img_index = 'lightbox'; } return ' rel="' . $img_index . '"'; } /** * Gets the preferred language for a user's email address. * * @param $address * The email address to check. * * @return * The language object to be used in translation, localization, etc. If a * user account can not be found for $address, language_default() is * returned. * * @see user_preferred_language() * @see language_default() */ function uc_store_mail_recipient_language($address) { // See if any user exists for this address. $account = user_load_by_mail(trim($address)); if ($account) { $lang_object = user_preferred_language($account); } // If not, site-wide default. else { $lang_object = language_default(); } return $lang_object; } /** * Displays prices in forms with a minimum number of decimal places. * * @param $price * The price to display as the #default_value in a form field. */ function uc_store_format_price_field_value($price) { $exact = rtrim(number_format($price, 6, '.', ''), '0'); $round = number_format($price, variable_get('uc_currency_prec', 2), '.', ''); if ($exact == rtrim($round, '0')) { return $round; } else { return $exact; } } /** * Executes hook_uc_form_alter() implementations. * * API function to invoke hook_uc_form_alter() implementations allowing those * modules to alter the form before the Drupal layer hook_form_alter() is * invoked. * * @see hook_uc_form_alter() */ function uc_form_alter(&$form, &$form_state, $form_id) { drupal_alter('uc_form', $form, $form_state, $form_id); }