Friday, 6 January 2012

Writing Drupal FieldMaps with Salesforce Suite

The real power of the Drupal Salesforce Suite module is in its ability to specifically connect Drupal objects and their individual fields, to their Salesforce counterparts. Out of the box, the module comes with a pre-configured "User" and "Page" FieldMap, which give you a good feel for the abilities of this functionality, but with a little creativity, all sorts of powerful data management can be achieved.

Examining the User FieldMapping

If you access the field maps list (admin/config/salesforce/fieldmap) and click to edit the "User" row, you can see that the Last name and Postcode of the Target (Salesforce) object are mapped to the Last name and Post code of the (Drupal) User object. That is how easy it is to tie standard fields together. My Drupal user object had a lot more fields to it though, and as you can see here, they have all been simply mapped over to Salesforce equivalents in the "Members" object.

Custom fields are listed at the bottom of the Salesforce fields select box, which, incidentally, is populated by the information provided by the WSDL file you uploaded.

I did note, during the process of adding multiple fields, that sometimes the Drupal overlay didn't refresh correctly. Occasionally I would have to "Save" and re-edit the mapping to get the rows to appear correctly. The easiest way I found to keep the table fresh was to re-click the "Edit" tab after each "Add" click, whether I'd seen the new row appear or not, as this refreshed the form, guaranteeing the new field would be visible.

Once you have completed this mapping, and made sure that you have ticked at least the "Create" checkbox at the top of the mapping page, if you register a new user on your Drupal website, it should appear in both your Drupal Users view AND in your contacts list. Your first synchronisation is complete! If you then navigate to your Drupal user page for the user (logged in as admin) you will see a Salesforce tab, which will detail the connection between the two objects.

As you can see here, the data for my Drupal user has been mapped to the relevant fields in Salesforce, and also there is an extra section detailing the Salesforce ID associated with the account.

Synchronising a Drupal object with Salesforce

One of the key pieces of functionality I had to deal with was recording user comments on the Drupal site in Salesforce. Luckily, "Comment" is a Drupal object already tied into the Salesforce suite, so by clicking "add" and then selecting "Comment: Poll comment" from the Drupal Object list was as simple as that.

My specification for this prototype was to register Comments as an "Event" within Salesforce. So from the Salesforce object list, I chose Event, and then began making my mappings. I was then involved in using a wide plethora of techniques to squeeze the relevant info out of Drupal and into Salesforce, as you can see from this screenshot:
(click to enlarge further)

For the "Location", "Type" and "Show Time as" fields, I used a Fixed value, so these are the same for all Event Objects created. In order to tell Salesforce to set the Start and End times as that instant, I used the "Evaluate PHP" option, and told it to return a new date object, formatted as "c" - which from the PHP Date API, you can see is a Unix timestamp - exactly as Salesforce is expecting.

The next fields were the trickiest. Whenever a new object is created by the Salesforce suite, because the SOAP connection is authenticated by the user login you provided during set-up, all new objects are created BY this account. Which means no matter who is logged into Drupal, the content is being created by "the system" in Salesforce. In order to connect the new content with the current Drupal user, I had to insert the following PHP snippet into the Lead ID field:

global $user;

$account = user_load($user->uid);

return $account->salesforce->sfid;

(Obviously, because of the textfield, this was all entered on one line)

This snippet retrieves the logged in user, loads up their (expanded) user account, and then returns the SFID (Salesforce ID) from it. I was able to use a similar technique to include the Users e-mail address, using "global $user; return $user->mail;" - which is otherwise unavailable to you in the drop-down boxes.

In order to group the comments against specific polls, I passed the comment subject into the Event subject field. In a custom drupal module, I used a form_alter to ensure the subject was the title of the poll, and I used the Comment settings to then hide the title field from the user.

function mymodule_form_alter(&$form, &$form_state, $form_id)

    if($form_id == "comment_node_poll_form")
        $form['subject']['#default_value'] = $form['#node']->title;

The body of the comment, obviously, was mapped to the description of the Event, and with that, all the information I collected in Drupal, was now also being stored in Salesforce.

 Tracking custom Drupal events in Salesforce

What do you do though, if you want to track a "customised" event in Salesforce..? Believe it or not, when a Drupal user votes in a Poll, for example, it doesn't actually create a Drupal object, and thus cannot be "sent" to Salesforce. In order to do this, I had to get a little more funky.

What I ended up doing, in a number of cases, was finding a relevant hook to the event, and then using it to trigger the programmatic creation of a custom object, which in turn trigged an exchange with Salesforce.

In the case of polls, there is a poll_vote hook, which, alongside a custom content type called "cck_poll_vote" allowed me to send Drupal votes to Salesforce.

In a custom module I wrote this hooking function that generates a cck_poll_vote each time a vote is cast:

function mymodule_vote($form_id, $form_values) {

    $poll = $form_values['complete form']['#node']->title;
    $choice = $form_values['complete form']['choice']['#options'][$form_values['complete form']['choice']['#value']];

    $body_text = 'A CCK Drupal object representing a poll vote.';

    $node = new stdClass();
    $node->type = 'cck_poll_vote';

    $node->title    = 'Poll Vote';
    $node->language = LANGUAGE_NONE;

    $node->field_poll_id[$node->language][0]['value'] = $poll;
    $node->field_choice[$node->language][0]['value'] = $choice;

    $node = node_submit($node);

It was then a simple case of creating a normal fieldMapping for a Drupal object, in the Salesforce Suite. cck_poll_vote automatically appears in the list of Drupal objects, and the mapping can be set to trigger whenever one is created, just like any other object.


  1. Thanks Simon, very useful article. :)

  2. Have you ever done mappings of multiple-select fields? I have problem with that because Salesforce returns all of the selected values in a string.

    For example, when you have multiple-select field in Salesforce with options selected:
    1. CD
    2. DVD

    When data are downloaded from Salesforce to Drupal, the format is just string like this:

    This fact makes problems for example when using Views to sort or filter nodes based on this field.

    Thanks a lot.

  3. Hi, I am using Drupal-6 with saleforce. Integrations completed as per your instructions. My requirement is follows,
    I want to integrate Customer Support Account Information on my drupal site.
    For that when a customer login to my drupal site, it check the active / inactive status in SalesForce and if the user is active then only the system redirect user to the My account page. How is this possible with the fieldmaping.

    Thanks a lot.
    George Telgy