CiviCRM and Drupal Commons
FosterClub, the national network for young people in foster care, is upgrading their Drupal 5 site to a Drupal 7/Commons distribution. In addition, to better manage the activities of their youth, caregivers, child welfare professionals and other concerned citizens, the site will include CiviCRM as a backend 'database' for member tracking, event coordination, mailings and more. The contributed module described here helps CiviCRM play nice with Commons' extended user profiles.
To keep costs down - both now and for future upgrades - FosterClub smartly wants to use both Commons and CiviCRM as they come "out of the box" as much as possible. There are some places that this mix could pose some challenges, as Commons and CiviCRM each have their own way of handling such things as groups, events and activities. Fortuntely, in this use case, there is a clear separatation of internal (CiviCRM) and external (Commons) activities, etc. so we neatly sidestep the issue in most cases.
One place that exhibited a minor but slightly problematic overlap was in the handling of use profiles. While items like capturing first and last name would normally be managed via CiviCRM custom profiles exposed in the user registration pages, Commons comes with profile entity fields pre-built for first and last names as well as a few other fields, such as social media URLs. As our desire is to keep Commons as vanilla as possible, we needed a way to sync these fields to CiviCRM during user-to-contact synchronization or - more commonly - when the user creates or updates their profile.
Fortunately, CiviCRM and Drupal have hooks that make this easy. A new CiviCRM contact record is created from a Drupal user account during user-to-contact synchronization or, more commonly, after a new Drupal user account is created. Once the CiviCRM contact record has been created, CiviCRM records the {Drupal_User_ID, CiviCRM_contact_id} association in the civicrm_uf_match table ('uf' stands for 'User Framework', and in our case the user framework is 'Drupal'). Immediately prior to the database write of this association, hook_civicrm_pre() is invoked with 'create' and 'UFMatch' arguments. As Drupal already has the First Name and Last Name fields populated, all we need to do is grab them and push them into the already created civicrm_contact record. Here's what the hook implementation looks like:
/** * Implements CiviCRM hook_civicrm_pre() */function civicrm_commons_civicrm_pre($op, $object_name, $object_id, &$object_ref) { if ($op == 'create' && $object_name == 'UFMatch') { // initial user creation and user->contact sync. $cid = $object_ref['contact_id']; $uid = $object_ref['uf_id']; if ($uid && $cid) { $user_obj = user_load($uid); _civicrm_commons_update('pre_UFMatch', $uid, $cid, $user_obj); } }}
The _civicrm_commons_update() function handles all the database wrangling - see the code for details.
That handles new contact creation. More commonly, when a Drupal user updates their account (perhaps with a new First Name) the Drupal hook_user_update() is invoked. At this point we know the Drupal User ID but not the associated CiviCRM contact ID, so we use the CiviCRM API to get the contact_id:
/** * Implements Drupal hook_user_update() * This is invoked when a user updates their profile. */function civicrm_commons_user_update(&$edit, $account, $category) { civicrm_initialize(); $uid = $account->uid; // get CiviCRM Contact ID from Drupal User ID $params = array('version' => 3, 'sequential' => 1, 'uf_id' => $uid, ); $result = civicrm_api('UFMatch', 'Get', $params); $cid = $result['values'][0]['contact_id']; if ($cid) { _civicrm_commons_update('user_update', $uid, $cid, $account); }}
This is all wrapped up in the civicrm_commons module. You can get the code with git using this command:
git clone --branch 7.x-1.x git.drupal.org:project/civicrm_commons.git
Installing the module brings an added bonus for anyone doing a large Drupal user -> CiviCRM contact synchronization. My server timed out after about 10K user-to-contact syncs, so this drush script batches the synchronize() process. For example, to sync 50K users in batches of 10K, run (from a bash shell):
for offset in 0 10000 20000 30000 40000; do drush civicrm-synchronize $offset 10000done
Topics