BLOG: Web Content Management

Welcome to Oshyn’s Web Content Management Blog where our experts discuss the latest developments and best practices in the Content Management industry with a focus on several leading platforms: Drupal, EPiServer, Jahia, Open Text and Sitecore.

Enterprise Drupal: Ubercart eCommerce Addresses and Location Module

Enterprise Drupal: Ubercart eCommerce Addresses and Location Module

Eric Aguayo... - Monday, March 01, 2010

In Drupal, the Location module is used to handle everything related to locations or addresses whether it is to associate locations with users or nodes. Ubercart ecommerce module, is used to handle website payments, and is widely used due to its ease of usability. The biggest business benefit is to provide an integration of both of these modules for a seamless ecommerce/payment processing experience.

The Ubercart module suite purpose is to provide a complete built-in solution to handle website payments and e-commerce solutions, which means that is probable that you not need to download any other plug-in module for Ubercart to handle a specific functionality. This solution might seem better for the Drupal user since the user might not have to install additional software to handle specific functionality as it might be provided one of the Ubercart built-in modules. However this represents some drawbacks, one of them is that if there is a bugfix on one of the Ubercart built-in modules we might need to wait until the next realease of the whole Ubercart module suite to get the module update and the other is that there might be some functionality that is being provided by other modules, such as the billing address. The Ubercart Drupal module has its own way to handle billing addresses, however there is a more specialized module to handle such things such as the Location module mentioned before.


Fortunately, Drupal flexibility through the hook system allows us to provide an integration between the Ubercart billing addresses and the location module by developing a custom module. Suppose that a client request is to give the users the ability to set the billing address (which is handled by Ubercart) as the same as the profile address (which is handled by the Location module). For this, we are going to include the location data attached to a node/user into the billing address of the recurring fees checkout page. First we need to understand some differences between ubercart and location module addresses storage. Ubercart store the province/state data by a province id called zone id which can be retrieved by the {uc_zones} table. The location module on the other hand retrieves the province/state data by returning the state code, which corresponds to the zone_code on the {uc_zones} table for Ubercart. Based on this, we write the following function to perform the conversion between the zone code to the zone id.

<?php
  
function uc_location_address_get_zone_id($zone_code)
  {
      
$query 'SELECT zone_id FROM {uc_zones} WHERE zone_code = "%s"';
      
$result db_query($query$zone_code);
      
$zone_id db_result($result);
      return 
$zone_id;
  }
?>



Something similar happens with the country where we can have the following function to perform the conversion. This is only necessary if we are using the country field for the billing address.

<?php
  
function uc_location_address_get_country_id($country_code)
  {
      
$country_code strtoupper($country_code);
      
$query 'SELECT country_id FROM {uc_countries} WHERE country_iso_code_2 = "%s"';
      
$result db_query($query$country_code);
      
$country_id db_result($result);
      return 
$country_id;
  }
?>


Now, we need to include this data as part of the checkout form so we can display the data on the ubercart fields. We are going to include this information in hidden fields so they can be optionally included as the billing address information. We can do this through the Drupal hook_form_alter which can be implemented in a module called uc_location_address

<?php
  
/**
   * Implementation of hook_form_alter
   */
  
function uc_location_address_form_alter(&$form$form_state$form_id)
  {
      global 
$user;
      
// We want to execute the following code only when the checkout form is loaded
      
if ($form_id == 'uc_cart_checkout_form') {
          
// We add a javascript code to optionally include the user/node address
          // as part of the billing address
          
drupal_add_js(drupal_get_path('module''uc_location_address') .
 
'/uc_location_address.js');
          
          
// We load the node or user containing the address we want to display
          // in this case we are using a node that can be added through the content
          // profile module.
          // For a user object (usually loaded through user_load function),
          // locations are tipically retrieved from $account->location
          
$profile_node node_load(array('uid' => $user->uid'type' => 'profile'));
          
          
// We add this information as part of the billing pane of checkout form.
          // It can be added directly to the fields but we are adding to different fields
          // so user can optionally choose to use the user address as billing addresses
          // which are not necessary the same
          
$form['panes']['billing']['profile_node']['street'] = array('#type' => 'hidden''#value' => $profile_node->locations[0]['street'], );
          
$form['panes']['billing']['profile_node']['additional'] = array('#type' => 'hidden''#value' => $profile_node->locations[0]['additional'], );
          
$form['panes']['billing']['profile_node']['city'] = array('#type' => 'hidden''#value' => $profile_node->locations[0]['city'], );
          
$form['panes']['billing']['profile_node']['province'] = array('#type' => 'hidden''#value' =>
uc_location_address_get_zone_id($profile_node->locations[0]['province']), );
          
$form['panes']['billing']['profile_node']['country'] = array('#type' => 'hidden''#value' =>
uc_location_address_get_country_id($profile_node->locations[0]['country']), );
          
$form['panes']['billing']['profile_node']['postal_code'] = array('#type' => 'hidden''#value' => $profile_node->locations[0]['postal_code'], );
          
          
// Finally, in our case we wanted to override the billing address select drop down
          // by a checkbox to indicate that we want the billing address to be the same address
          // as in profile information.
          // We also tie to a javascript function triggered on the onchage event.
          
$form['panes']['billing']['billing_address_select'] = array('#type' => 'checkbox''#description' =>
t('Billing Address same as profile information'), '#attributes' =>
  array(
'onchange' => 'uc_location_address_select();'), );
      }
  }
?>


Finally we add some javascript code to the uc_location_address.js file to handle the dynamic fill of the fields.

function uc_location_address_select()
  {
      
// check if the option has been selected, if so copy all fields values
      
if ($("#edit-panes-billing-billing-address-select") . attr("checked") == true) {
          $(
"#edit-panes-billing-billing-street1") . val($("#edit-panes-billing-profile-node-street") . val());
          $(
"#edit-panes-billing-billing-street2") . val($("#edit-panes-billing-profile-node-additional") . val());
          $(
"#edit-panes-billing-billing-city") . val($("#edit-panes-billing-profile-node-city") . val());
          $(
"#edit-panes-billing-billing-zone") . val($("#edit-panes-billing-profile-node-province") . val());
          $(
"#edit-panes-billing-billing-country") . val($("#edit-panes-billing-profile-node-country") . val());
          $(
"#edit-panes-billing-billing-postal-code") . val($("#edit-panes-billing-profile-node-postal-code") . val());
          
// prevent the fields from being edited
          
$("#edit-panes-billing-billing-street1") . attr("disabled"true);
          $(
"#edit-panes-billing-billing-street2") . attr("disabled"true);
          $(
"#edit-panes-billing-billing-city") . attr("disabled"true);
          $(
"#edit-panes-billing-billing-zone") . attr("disabled"true);
          $(
"#edit-panes-billing-billing-country") . attr("disabled"true);
          $(
"#edit-panes-billing-billing-postal-code") . attr("disabled"true);
      } else {
          
// In case the option has been deselected, clear and enable the fields
          
$("#edit-panes-billing-billing-street1") . val("");
          $(
"#edit-panes-billing-billing-street2") . val("");
          $(
"#edit-panes-billing-billing-city") . val("");
          $(
"#edit-panes-billing-billing-zone") . val("");
          $(
"#edit-panes-billing-billing-country") . val("");
          $(
"#edit-panes-billing-billing-postal-code") . val("");
          $(
"#edit-panes-billing-billing-street1") . removeAttr("disabled");
          $(
"#edit-panes-billing-billing-street2") . removeAttr("disabled");
          $(
"#edit-panes-billing-billing-city") . removeAttr("disabled");
          $(
"#edit-panes-billing-billing-zone") . removeAttr("disabled");
          $(
"#edit-panes-billing-billing-country") . removeAttr("disabled");
          $(
"#edit-panes-billing-billing-postal-code") . removeAttr("disabled");
      }
  }


Conclusions and Recommendations


We can provide integration between contributed and core modules through the Drupal hook system without the need of hacking into the core functionality of the modules. It is important that contributed module developers leverage the functionallity of existing Drupal modules so the code and/or data duplication is reduced and extra effort in integrating solutions is avoided.

The code presented here provides a very specific functionality which can help to solve a very specific case but does not work as a general drupal module or provide a seamless integration between Ubercart addresses and Location module. However, I believe this can be the start of a more general module which can be further uploaded to the community. I have already filled a feature request on the issue queue of the Ubercart module http://drupal.org/node/520484 which stills active but hopefully some day a location integration module will be added to the Ubercart modules suite.
ajax rotator
Recent Posts

RSS feeds
Tag cloud
web3.0 website design los angeles licencing sitecore meetup google appEngine mashups mashup mashware LDAP management social media boost print Ubercart design reddot mysql open source php lucene open text management server cms white paper GIS theming Navigation dynament architecture drools oshyn Sitecore asp facebook app, OS 3.0, three20 geo tuckey rdbs performance reddot white paper GSA ASP.NET search suggest LiveServer presentation layer icefaces CMS Training sitecore oms development ajax command line sitecore email campaign manager plugin Web development google analytics facebook developers thoughts google call to action HubSpot campaigns java jahia wcm keywords new sites communicating web evolution liferay .net design patterns geographical php5 profile box MVC jquery, jquery plugin content mangement Actionscript 3 JSR-168 keyword selection consulting opentext Sitecore Active Directory ui concrete5 lead management web design sitecore online marketing suite, online marketing suite web design los angeles software design sitecore layout CTA frontend optimization IT Investment foursquare sitecore devices Flash open text web solutions search engine content management los angeles css jahia cms Solr cloud computing EPiServer Active Directory templating EPiServer cookie-free domain redundant database structure multisite CRM optimization web development los angeles content authors location cms whitepaper postgis google search appliance OO Development open text liveserver inbound marketing jsp open text spatial twitter Jahia sitecore ECM VS2010 Acquia open text cms portlets open text delivery server AS3 WCM Design Patterns missing images LS cms los angeles, content management los angeles, web development los angeles, website design los angeles, web design los angeles, sitecore meetup, sitecore user group reddot cms higher education web2.0 Delivery Server RFP project management Sharepoint memcache web marketing tuning cms los angeles online marketing web marketing for dummies facebook friendly url E-commerce SMM drush social network Active Directory html linux Live Server Visual Basic jquery template design content management systems wcm sales 2.0 cloud reddot cms code design data access lead generation target dynament marketing integration cluster scalability VPP release management enterprise Visual Studio Ubuntu Server multilingual url mobile ubuntu modules IIS google maps deployment patterns content authoring Drupal open-source Maven configuration management CMS VB SEO Dynamic Data sitecore user group Business Users reddot whitepaper web services industry challenges CMS usability ipc ASP.NET MVC fbml webcomponent editing content content management whitepaper Velocity iphone higher education Marketing Automation reddot higher education social 2.0 tools javascript portal APC content management white paper web content management Database oms profile tab ajax push
2010 Copyright Oshyn. All rights reserved.