Creating an Address block token

Veröffentlicht
2010-08-17 16:03
Written by
michaelmcandrew - member of the CiviCRM community - view blog guidelines

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
Filed under

Comments

You mention that the address formatting has been solved in the mailing label.

Wouldn't it be easier to fix it in the PDF Letter (or wherever it is not working) instead of creating a new token ?

Alternatively, using the token you created in all the places civicrm generates an address ?

X+

Hi Michael,

This is a nice write up - great to see you talking through the process.

It's kind of timely for me as I have been writing a token hook for a customer whose requirements are to be able to print a one page report (and I'm treating this as a mail merge type report as you mentioned) with details for various contacts including standard fields and:
- names of all household members
- Total contributions made
- Total Pledges made
- Last 5 contributions made
- Date of last contribution
- end date of last pledge

I have written a hook that exposes 6 different tokens - one for each of the lines above but I've been wondering about the performance implications of having all 6 in one module. Are all 6 tokens generated for all contacts everytime a pdf letter / e-mail or hook-using communique is generated - or is the hook only called if one or more tokens from it are in the communique

another way to get around the multiple blank line issue is to do some preg_replace:

replace '\s*'. PHP_EOL .'\s*' (a line break enclosed in whitespace of some kind) with PHP_EOL (a single line break)
and replace ' + +' (multiple spaces) with ' ' (a single space)