Address block tokens

Creating a nicely formatted address block with CiviCRM

17th August 2010

Tokens are used in CiviCRM to create mail merges in much the same way as, for example, Microsoft Office. They are currently implemented in (at least) four places in CIviCRM: 'CiviMail', 'Send Mail to Contacts', 'Create PDF Letter' and 'Create Mailing Labels'.

Out of the box Civi comes with a decent set of tokens, including tokens for all the address fields. One thing it doesn't do is provide a token that correctly formats an address block taking account of when fields aren't present. For example, if i used the following address tokens for my address:

{contact.street_address}
{contact.supplemental_address_1}
{contact.supplemental_address_2}
{contact.city}
{contact.postal_code}

It would look something like this:

109 Roding Road

London
E5 0DR

i.e. two blank lines make a big gap which doesn't look great.

Civi has already solved the address formatting problem in other places including mailing labels and on the contact view screen. And Civi has a couple of hooks that can be used to define new tokens: hook_civicrm_tokens and hook_civicrm_tokenValues.

I didn't think it would be too difficult to put these together so I decided to have a go. Here is the dummy's guide...

First I set up a new local development environment with the latest version of Drupal and CiviCRM and the sample data so I had some data to play with.

Then I started a Drupal module for my two hook implementations, put it under revision control, and (because I thought this might be useful to others in CiviLand) (there's no time like the present) put it on GitHub.

To make things easy and to make sure my hooks were firing properly, I pasted in some code that I knew would work from civitest.module.sample (which is included in every CiviCRM download at civicrm/drupal/civitest.module.sample).

I then removed all the tokens I didn't need from hook_civicrm_tokens and simplfied hook_civicrm_tokenValues to contain some placeholder text and tested the hook (you can follow progress on GitHub). All seemed to be working fine.

The last piece of the puzzle was retrieving the formatted address block. I knew this code was fired as part of the mailing labels task. So I looked at the list of tasks defined in CRM/Contact/Task.php. Following the trail in that file, I had a look at the class CRM_Contact_Form_Task_Label (in CRM/Contact/Form/Task/Label.php).

After a bit of reading and poking around in CRM_Contact_Form_Task_Label, I realised that the address formatting was handled by a call to this function: CRM_Utils_Address::format. Adding a quick print_r($row) at the right place in CRM/Contact/Form/Task/Label.php showed me that the $fields in CRM_Utils_Address::format was expecting a list of fields that looked pretty similar to those returned by the api call civicrm_contact_get. The rest of the variables that CRM_Utils_Address::format expects defaulted to null and I was feeling lucky so edited my hook to make a call for civicrm_contact_get for each contact, and feed that into CRM_Utils_Address::format.

That outputted something pretty close to what I wanted. All that remained was to wrap the output with the php function nl2br() which as you might expect converts the line breaks into s that the pdf engine respects and the alpha version was finished!

You can download it here: http://github.com/michaelmcandrew/civicrm-addressblocktoken

A couple of points to note:

  • This module returns the primary address for the contact (as does 'CiviMail' and 'Create PDF letter'). You can't use it to retrieve other location types (though it wouldn't be too hard to extend it to do so).
  • Part of the motiviation here was to write a simple tutorial on creating a module (done!) but this kind of code probably sits better in core (todo).
  • It's alpha - i.e. I might have committed some outrageous coding sins. Please have a look and let me know if so :)
  • Background to using tokens in CiviCRM