hook

Defining customer profile weights in Drupal Commerce

Drupal Commerce is a great leap forward from Ubercart. It’s especially heartening to see it use so much of other APIs - Views, Rules etc. - rather than doing its own thing. However, we think we’ve found a bug in the way that it sorts the fields and panes on the checkout page.

The commerce_customer sub-module takes customer profile types (defined via hook_commerce_customer_profile_type_info()) and prepares them for inclusion in the checkout pane. When doing so, it also transfers a weight field called checkout_pane_weight, so that in theory these types can be weighted (to make e.g. contact form appear before billing address form).

However, checkout_pane_weight is not used anywhere else in the commerce codebase as far as I can see. When commerce_checkout_panes() merges in an array of defaults, it includes a simple ‘weight’ field, but not ‘checkout_pane_weight’.

Until this bug is fixed, you can work around it with the following hook in your own module:

<?php
/** 
 * Implements hook_commerce_checkout_pane_info_alter() 
 */
function MYMODULE_commerce_checkout_pane_info_alter(&$checkout_panes) {  
  // Get weights from hook_commerce_customer_profile_type_info
  $pane_weights = cscommerce_commerce_customer_profile_type_info();
  // Loop over them and weight the assembled panes accordingly
  foreach($pane_weights as $pane_key_suffix => $weight_config) {
    $pane_key = "customer_profile_$pane_key_suffix";
    if (array_key_exists($pane_key, $checkout_panes)) {
      $checkout_panes[$pane_key]['weight'] = $weight_config['checkout_pane_weight'];
    }  
  }
}

Note that, because commerce_checkout_panes() increments pane weights by a single digit each time, you don’t get much wiggle room when you’re trying to weight your own panes and they can easily end up all at the top or all at the bottom. 

So if, like me, you just want to swap round two panes, and the single-digit weight increments are messing things up, here’s an even quicker workaround:

<?php
/**
 * Implements hook_commerce_checkout_pane_info_alter()
 */
function MYMODULE_commerce_checkout_pane_info_alter(&$checkout_panes) {
  if ($checkout_panes['customer_profile_billing']['weight'] 
      > $checkout_panes['customer_profile_contact']['weight']) {
    list($checkout_panes['customer_profile_billing']['weight'],
         $checkout_panes['customer_profile_contact']['weight']) =
      array($checkout_panes['customer_profile_contact']['weight'],
          $checkout_panes['customer_profile_billing']['weight']);
  }
}

This is a simple function that swaps the two weights around in a single assignment step, and it means that if you have weights of 20,19,18,17,16… then you can seamlessly swap your two profiles around to be 20,19,17,18,16… without any recalculation.

Blog category:

Render any block in Drupal 5

Blocks are a bit neglected in Drupal: here's one way to use them more flexibly

Blocks aren't what you might call "first-class citizens" in Drupal 5 or 6 (or 7, as far as I'm aware). Block functionality is provided by a range of functions and database tables, but ultimately there's no thingness, no Ding an sich tying them together as an object the way that a node of content or a user might be.

Annoyingly, there's no function in Drupal 5 to grab a block and render it. You can render a region, a block container which then makes a number of decisions for you about what blocks should appear in it. Blocks can in principle be uniquely identified by (a) the module which provides the block through its implementation of hook_block() and (b) the "delta", the unique (potentially non-numeric) ID for each block in a given module. Yet there are no functions in the core block module to handle this.

One option is to move to Panels, which I'm doing for this site. Yet if your existing site is heavily based around blocks and regions, implementing Panels can be a lot of overhead just to render a single block in a template.

Here's a function which returns a single block, given the module name and block delta. It's an abstraction from block_list(), the function which returns all the blocks in a given region. You can put this in a shell module with an info file (see the previous post on theme preprocess hooks in Drupal 5 for how to quickly set up an otherwise empty module) or if you're not comfortable with that you can always name it accordingly and put it in template.php

/**
 * Get a single block for themeing
 */
function mymodule_get_block($module, $delta) {
  // User roles mean blocks are still invisible if the user
  // isn't permitted to see them
  global $user;
  $rids = array_keys($user->roles);
  $placeholders = implode(',', array_fill(0, count($rids), '%d'));
 
  // This is still cropped from block_list() , but newlines added
  // for legibility and blogposting
  $result = db_query(
      "SELECT DISTINCT b.* "
    . " FROM {blocks} b LEFT JOIN {blocks_roles} r "
    . "   ON b.module = r.module AND b.delta = r.delta "
    . " WHERE (r.rid IN ($placeholders) OR r.rid IS NULL) "
    . "   AND b.module = '%s' "
    . "   AND b.delta = '%s' "
    . " ORDER BY b.region, b.weight, b.module", 
    array_merge($rids, array($module,$delta)));
 
  // Assemble block info from the module
  while ($block = db_fetch_object($result)) {
    // Invoke the block hook in the supporting 
    // module and convert the return array
    $array = module_invoke($block->module, 'block', 'view', $block->delta);
    if (isset($array) && is_array($array)) {
      foreach ($array as $k => $v) {
        $block->$k = $v;
      }
    }
 
    // Swap in any user-defined title in admin interface
    if ($block->title) {
      $block->subject = $block->title == '<none>' ? '' : check_plain($block->title);
    }
 
    return $block;
  }
}

Once this is safely out of the way you can grab and render a block. The following code grabs the fourth block that you ever created through the block admin interface (the "block" module is the "maintainer" of those blocks, and the blocks are numbered starting at zero, hence "3"):

<?php print theme('block', mymodule_get_block('block', 3)); ?>

This is a bit unsafe, though. What if you disable mymodule? The template will break, and depending on your server configuration your visitors might see a pretty ugly error. So use the theme preprocess hooks trick. If you want the block to show in node.tpl.php then you're going to have to bite the bullet and put the bigger code snippet above in a module. Then, in the same module, you can write a hook_preprocess_node as follows:

function mymodule_preprocess_node(&$vars) {
  $vars['myblock'] = theme('block', mymodule_get_block('block', 3));
}

Then you can put just the bare variable in your node.tpl.php, which is much safer:

<?php print $myblock ?>

Turn off mymodule, and this just prints an empty string.

So, ta-da, blocks are now better exposed in your Drupal backend, for you to use in different places in your themeing. Unfortunately that still doesn't make them first-class citizens: they can't be categorized or given an "owner" i.e. an associated user ID; they can't be made to link to nodes in the same robust way that nodes can link to each other with node reference; they certainly can't have any CCK fields hung off them. But, humble though blocks are, they've still got some life left in them yet.

Blog category:

User loading and saving in Drupal 6.x

Nearly a year ago I broke down user_load() and user_save() in Drupal 5. I had to put together workflows for a number of jobs, specifically integrating the creation, instantiation and updating of users with an external system. Fast forward nearly twelve months, and we have to do it all over again for D6, for different work. So here's a PDF of user_load() and user_save() in Drupal 5 and 6.

flowcharts of user_load and user_save

The flowcharts have been especially useful in coding in the most Drupalish way possible. Drupal core (and well-behaved modules) is built with a hook-based architecture. That means that before and/or after important events, Drupal calls all the functions which follow a particular naming convention: any module which, in effect, implements a hook. That means your code can tag along with Drupal’s powerful core, making hook essential to developing modules efficiently.

What's changed between Drupals 5 and 6? Not much, to be honest:

  • Loading now tries to grab an object, rather than checking if an ID has been returned by the database first
  • Updating clears the sessions for newly-blocked users, effectively kicking them out; it also sends notification emails through _user_mail_notify
  • Creating doesn't grab a new ID for the user, pre-creation, owing to D6's better database abstractions

For your convenience and mine, all six workflows are now in the same PDF. That makes it easier to compare 5 and 6 side by side, but it also makes clear some of the very minor errors I made in the original Drupal 5 diagrams. Well, best let them stand, for transparency's sake. And besides: if a man's errors are his portals of discovery, you'd be lucky to fit the chipmunk of serendipity through these.

The multiple magics of Drupal search

Form API is magical; core Drupal search is a twist on that magic; hooking onto that twist puts your code on yet another level of weird.

Drupal's Form API handles so much work for you that you'd be a fool not to use it as much as possible. This code snippet:

function myform_some_form($form_state) {
  $form['text'] = array(
    '#type' => 'textfield',
    '#title' => t('Your submission'),
    '#default_value' => t('Enter some text'),
    '#description' => t('Please use this field to submit some text'),
    '#required' => TRUE,
  );
  return $form;
}

creats a form with:

  • A single textfield element
  • Accessible XHTML with form labels
  • Potentially localized labels, translated into any number of languages
  • A bit of similarly localized help text below the element
  • Validation of the form submission, with the field content marked as required

That's a separate item of form functionality for each array key. And as long as you use Form API, Drupal handles validation and input sanitization for you, thus massively reducing the risk of attack by SQL injection or XSS.

Bookmarkable search URLs with POSTed search terms

But there's a catch. To encourage best practice in terms of form submission and friendly URLs, Form API defaults to HTTP POST. If site searching used Form API (which it does) then what impact would that have? Successful searches could never be bookmarked, because the URL on its own doesn't capture the POST submission.

The search module tackles this by adding an extra twist to Form API. At the end of submission processing are the following two actions:

  • Call either the function named in $form['#submit'] or $ID_submit, where $ID is typically the name of the original form creation function ("myform_some_form" above)
  • Finally, either return to the original action page of the form, or redirect to any URL specified in $form['redirect']

The search module therefore uses a function called search_form_submit to grab the POSTed search terms, and redirect the user to search/$SEARCH_TYPE/$SEARCH_TERMS. $SEARCH_TYPE is "node" for Drupal's out-of-box textual node searching, but if you install some other search module e.g. Apache Solr then it'll be e.g. "apachesolr_search" instead. Result: bookmarkable search URLs.

Writing your own module to handle searches

This has important ramifications if you're trying to piggyback off core search somehow: if, say, you're still using core search or a third-party module for the actual result-finding, but then you want a page other than core search to display the results.

If you want the main site search form to redirect to your own pages, for example, then you have to (a) add your own $form['#submit'] function to the stack and then (b) use that to change the core search's $form['redirect']:

// Implementation of hook_form_alter(), adding an extra submit callback to
// search forms identified by their existing callback
function mysearch_form_alter(&$form, $form_state, $form_id) {
  $submits = array(
    'box' => 'search_box_form_submit',
    'form' => 'search_form_submit',
  );
  if (is_array($form['#submit'])) {
    $which = array_intersect($submits, $form['#submit']);
    $which && ($form['#submit'][] = 'mysearch_form_mysubmit');
  }
}
// Submit callback, which changes the redirect using a regular-expression replace
function mysearch_form_mysubmit(&$form, $form_state) {
  $form_state['redirect'] = preg_replace('/^search\/[^\/]+/', 'search/my_special_search',
    $form_state['redirect']);
}

Now you've got all your site search forms redirecting to a bookmarkable page at search/my_special_search/$SEARCH_TERMS. All you have to do now is write a menu callback for that page: from here on in you're on your own for now.

User loading and saving in Drupal 5.x

Workflows of Drupal's user load and save functionality: spot the hooks and win a programmatical prize.

Recently at Torchbox we’ve been looking into how to build extra functionality on top of Drupal users. The standard Drupal user object is a combination of the contents from the users table, plus any properties provided by the core profile module. This means that the Drupal user is a combination of rows (and admittedly deserialized, structured data) from a couple of tables in a relational database.

flowcharts of user_load and user_save

That works just fine for most purposes, but we may have to bring in content from not just outside the core Drupal tables but outside the core database, and even on a remote server through webservices. To this end we’ve decomposed the core user module’s user_load() and user_save() functions. This helps us understand better both the workflow and at what points in it our own code can motor into life, query all those extra resources (or set those queries in motion), assemble the rest of the user++ object, and then hand control back over to Drupal.

For those who don’t know much about Drupal, its core has a hook-based API structure. At certain points in its workflow, it checks all the modules for functions following certain naming conventions (typically the module name followed by the hook name e.g. mymodule_init on response startup, or mymodule_block to return details about the module’s support for Drupal page furniture). Any matching hook functions are called in the order defined by module weightings, and then page processing will generally continue: you can crowbar a grind-to-a-halt exit() in your hook, but it’d be unwise, as you can never be sure what tidying up Drupal might need to do after your hook. Outside these hooks, your code has little control over Drupal’s core functioning, unless you stub out entirely the bits of core you need yourself, and get your request to use those bits instead.

Because of the way they let your code tag along with Drupal’s powerful core, hooks are essential to developing modules in the most Drupalish way. With that in mind, here are flow diagrams of the three basic aspects of user functionality—create new, load, save existing—lifted straight from examination of the code:

Although you have to save a user before you can load them, I’ve put this functionality first in the above (admittedly unordered) list. There are two main reasons for this:

  1. user_save actually calls user_load a number of times, once or twice, to “refresh” the user object
  2. user_load is a more primitive function and so bears examination first

Stripped down, user_load consists of: querying the database for a core user record matching the search criteria; returning this and the extended profile data; unserializing a free-data field and inserting it into the user object; discovering user roles; triggering hook_user('load') and returning the object (or boolean false, if no user found).

What this reveals (which I didn’t realise before) is that the anonymous user is in the Drupal users table, with ID=0. Otherwise, searching for this user would return no records, and the anonymous user object could not be instantiated. You could therefore attach rich data to the anonymous user, if you were in a hacky mood.

The two user_save workflows are fairly similar. Creating a user means obtaining an ID from the database: because some MySQL providers have poorer feature sets than others, referential integrity is ensured at the application level rather than the database level. In place of obtaining an ID, user update calls hook_user('update') to pre-process the user. Both workflows then set aside special fields, such as the user’s password, user roles and any profile fields managed by that module (determined from user_fields()). Then they save this data into the database in slightly different orders, with user creation calling hook_user('insert') early on, and the update procedure calling hook_user('after_update') much later in the process, just before determining the external authentication mappings (e.g. OpenID) and returning the user object.

What does this mean for us? Well, we’ll want varying amounts of data to piggyback on the core user object, so we have somewhere to cache it. Ideally this data won’t be summoned—brought out of the distributed data ‘cloud’—on every request/response cycle, so we’ll need to do some local cacheing, but not so much that we’ll get out of synch with the cloud (or that we’ll duplicate sensitive data). We think that, given the pair of hooks in user_save for existing users, we’ll have just enough leverage to do this: the first hook will effectively “tear down” our extra data, so we can do what we want with it, and store it somewhere temporarily; the second hook will “set up” the user for the rest of the request, by putting all that data back in. The existence of user_load within user_save complicates things somewhat, but at the same time it gives us some more wiggle room, because each call to that function fires another hook.

A Drupal hook is worth a thousand lines of module code, but they’re still a bit few and far between for some workflows. Hopefully the accompanying images will help anyone reading to find them, and ditch those thousand lines before they’re even written.

My first Drupal.org documentation

Drupal has a built-in form abstraction system called Form API (or FAPI), which like the Atom protocol consists of two complementary halves. The first is an abstraction of forms to structured data, and is dealt with very well by both online documentation and the excellent Pro Drupal Development. The second is a workflow for turning this data into forms, validating form submissions, and processing valid submissions onto output streams such as the database, the browser or emails to the user.

This second half is quite complex and, like much of Drupal, has various points at which the user can hook into the process: whether by use of the hook_* functions that modules use to talk to each other and the core, or by defining #callback array elements within the FAPI structured data. However, documentation is pretty sparse: PDD is rather quiet on complex workflows, although it does have a lot of slightly higher-level, prose information.

Someone had already taken steps to rectify this: drupal.org user KarenS sent round a detailed flowchart of FAPI’s functional internals which detailed the function-by-function process of FAPI. Seeing a definite niche for a Tufte amateur such as myself, I muscled in and helped her out with a cleaner version of the diagram. I’m dead chuffed to say that this was good enough to go live: take a gander at the Form Workflow Illustration.

I hope to make similar diagrams for core Drupal (in less depth) and PHPTemplate at some point, actually all my own work: watch this space.

Subscribe to RSS - hook