Oct 28 2019
Oct 28

The Drupal 8 version of the Salesforce Suite provides a powerful combination of features that are ready to use and mechanisms for adding custom add-ons you may need.  What it does not yet have is lots of good public documentation to explain all those features.

A recent support issue in the Salesforce issue queue asked for example code for writing queries. While I’ll address some of that here, there is ongoing work to replace the query interface to be more like Drupal core’s.  Hopefully once that’s complete I’ll get a chance to revise this article, but be warned some of those details may be a little out of date depending on when you read this post.

To run a simple query for all closed Opportunities related to an Account that closed after a specific date you can do something like the following:

      $query = new SelectQuery('Opportunity');
      $query->fields = [
      $query->addCondition('AccountId', $desiredAccountId, '=');
      $query->conditions[] = [
        "(StageName", '=', "'Closed Won'",
        'OR', 'StageName', '=', "'Closed Lost')",
      $query->conditions[] = ['CloseDate', '>=', $someSelectedDate];
      $sfResponse = \Drupal::service('salesforce.client')->query($query);

The class would need to include a use statement for to get Drupal\salesforce\SelectQuery; And ideally you would embed this in a service that would allow you to inject the Salesforce Client service more correctly, but hopefully you get the idea.

The main oddity in the code above is the handling of query conditions (which is part of what lead to the forthcoming interface changes). You can use the addCondition() method and provide a field name, value, and comparison as lie 10 does. Or you can add an array of terms directly to the conditions array that will be imploded together. Each element of the conditions array will be ANDed together, so OR conditions need to be inserted the way lines 11-14 handle it.

Running a query in the abstract is pretty straight forward, the main question really is what are you going to do with the data that comes from the query. The suite’s main mapping features provide most of what you need for just pulling down data to store in entities, and you should use the entity mapping features until you have a really good reason not to, so the need for direct querying is somewhat limited.

But there are use cases that make sense to run queries directly. Largely these are around pulling down data that needs to be updated in near-real time (so perhaps that list of opportunities would be ones related to my user that were closed in the last week instead of some random account).

I’ve written about using Drupal 8 to proxy remote APIs before. If you look at the sample code you’ll see the comment that says: // Do some useful stuff to build an array of data.  Now is your chance to do something useful:

namespace Drupal\example\Controller;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Cache\CacheableJsonResponse;
use Drupal\Core\Cache\CacheableMetadata;
class ExampleController extends ControllerBase {
    public function getJson(Request $request) {
        // Securely load the AccountId you want, and set date range.
        $data = [];
        $query = new SelectQuery('Opportunity');
        $query->fields = [
        $query->addCondition('AccountId', $desiredAccountId, '=');
        $query->conditions[] = [
            "(StageName", '=', "'Closed Won'",
            'OR', 'StageName', '=', "'Closed Lost')",
        $query->conditions[] = ['CloseDate', '>=', $someSelectedDate];
        $sfResponse = \Drupal::service('salesforce.client')->query($query);
    if (!empty($sfResponse)) {
        $data['opp_count'] = $sfResponse->size();
        $data['opps'] = [];
        if ($data['opp_count']) {
            foreach ($sfResponse->records() as $opp) {
                $data['opps'][] = $opp->fields();
    else {
      $data['opp_count'] = 0;
    // Add Cache settings for Max-age and URL context.
    // You can use any of Drupal's contexts, tags, and time.
    $data['#cache'] = [
        'max-age' => 600, 
        'contexts' => [
    $response = new CacheableJsonResponse($data);
    return $response;

I left out a couple details above on purpose. Most notable I am not showing ways to get the needed SFID for filtering because you need to apply a little security checking on your route/controller/service. And those checks are probably specific to your project. If you are not careful you could let anonymous users just explore your whole database. It is an easy mistake to make if you do something like use a Salesforce ID as a URL parameter of some kind. You will want to make sure you know who is running queries and that they are allowed to see the data you are about to present. This is on you as the developer, not on Drupal or Salesforce, and I’m not risking giving you a bad example to follow.

Another detail to note is that I used the cache response for a reason.  Without caching every request would go through to Salesforce. This is both slower than getting cached results (their REST API is not super fast and you are proxying through Drupal along the way), and leaves you open to a simple DOS where someone makes a bunch of calls and sucks up all your API requests for the day. Think carefully before limiting or removing those cache options (and make sure your cache actually works in production).  Setting a context of both URL and User can help ensure the right people see the right data at the right time.

Oct 17 2019
Oct 17

The Drupal 8 Salesforce Suite allows you to map Drupal entities to Salesforce objects using a 1-to-1 mapping. To do this it provides a series of field mapping types that allow you to select how you want to relate the data between the two systems. Each field type provides handling to help ensure the data is handled correctly on each side of the system.

As of this writing the suite provides six usable field mapping types:

  • Properties — The most common type to handle mapping data fields.
  • Record Type — A special handler to support Salesforce record type settings when needed.
  • Related IDs — Handles translating SFIDs to Drupal Entity IDs when two objects are related in both systems.
  • Related Properties — For handling properties across a relationship (when possible).
  • Constant — A constant value on the Drupal side that can be pushed to Salesforce.
  • Token — A value set via Drupal Token.

There is a seventh called Broken to handle mappings that have changed and need a fallback until its fixed. The salesforce_examples module also includes a very simple example called Hardcoded the shows how to create a mapping with a fixed value (similar to, but less powerful than, Constant field).

These six handle the vast majority of use cases but not all.  Fortunately the suite was designed using Drupal 8 annotated plugins , so you can add your own as needed. There is an example in the suite’s example module, and you can review the code of the ones that are included, but I think some people would find an overview helpful.

As an example I’m using the plugin I created to add support for related entities to the webform submodule of the suite (I’m referencing the patch in #10 cause that’s current as of this writing, but you should actually use whatever version is most recent or been accepted).

Like all good annotated plugins to tell Drupal about it all we have to do is create the file in the right place. In this case that is: [my_module_root]/src/Plugins/SalesforceMappingField/[ClassName] or more specifically: salesforce_webform/src/Plugin/SalesforceMappingField/WebformEntityElements.php

At the top of the file we need to define the namespace, add some use statements.

namespace Drupal\salesforce_webform\Plugin\SalesforceMappingField;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\salesforce_mapping\Entity\SalesforceMappingInterface;
use Drupal\salesforce_mapping\SalesforceMappingFieldPluginBase;
use Drupal\salesforce_mapping\MappingConstants;

Next we need to provide the required annotation for the plugin manager to use. In this case it just provides the plugin’s ID, which needs to be unique across all plugins of this type, and a translated label.

 * Adapter for Webform elements.
 * @Plugin(
 *   id = "WebformEntityElements",
 *   label = @Translation("Webform entity elements")
 * )

Now we define the class itself which must extend SalesforceMappingFieldPluginBase.

class WebformEntityElements extends SalesforceMappingFieldPluginBase {

With those things in place we can start the real work.  The mapping field plugins are made up of a few parts: 

  • The configuration form elements which display on the mapping settings edit form.
  • A value function to provide the actual outbound value from the field.
  • Nice details to limit when the mapping should be used, and support dependency management.

The buildConfigurationForm function returns an array of form elements. The base class provides some basic pieces of that array that you should plan to use and modify. So first we call the function on that parent class, and then make our changes:

   * {@inheritdoc}
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $pluginForm = parent::buildConfigurationForm($form, $form_state);
    $options = $this->getConfigurationOptions($form['#entity']);
    if (empty($options)) {
      $pluginForm['drupal_field_value'] += [
        '#markup' => t('No available webform entity reference elements.'),
    else {
      $pluginForm['drupal_field_value'] += [
        '#type' => 'select',
        '#options' => $options,
        '#empty_option' => $this->t('- Select -'),
        '#default_value' => $this->config('drupal_field_value'),
        '#description' => $this->t('Select a webform entity reference element.'),
    // Just allowed to push.
    $pluginForm['direction']['#options'] = [
      MappingConstants::SALESFORCE_MAPPING_DIRECTION_DRUPAL_SF => $pluginForm['direction']['#options'][MappingConstants::SALESFORCE_MAPPING_DIRECTION_DRUPAL_SF],
    $pluginForm['direction']['#default_value'] =
    return $pluginForm;

In this case we are using a helper function to get us a list of entity reference fields on this plugin (details are in the patch and unimportant to this discussion). We then make those fields the list of Drupal fields for the settings form. The array we got from the parent class already provides a list of Salesforce fields in $pluginForm[‘salesforce_field’] so we don’t have to worry about that part.  Since the salesforce_webform module is push-only on its mappings, this plugin was designed to be push only as well, and so limits to direction options to be push only. The default set of options is:    

'#options' => [
    MappingConstants::SALESFORCE_MAPPING_DIRECTION_DRUPAL_SF => t('Drupal to SF'),
    MappingConstants::SALESFORCE_MAPPING_DIRECTION_SF_DRUPAL => t('SF to Drupal'),
    MappingConstants::SALESFORCE_MAPPING_DIRECTION_SYNC => t('Sync'),

And you can limit those anyway that makes sense for your plugin.

With the form array completed, we now move on to the value function. This is generally the most interesting part of the plugin since it does the work of actually setting the value returned by the mapping.

   * {@inheritdoc}
  public function value(EntityInterface $entity, SalesforceMappingInterface $mapping) {
    $element_parts = explode('__', $this->config('drupal_field_value'));
    $main_element_name = reset($element_parts);
    $webform = $this->entityTypeManager->getStorage('webform')->load($mapping->get('drupal_bundle'));
    $webform_element = $webform->getElement($main_element_name);
    if (!$webform_element) {
      // This reference field does not exist.
    try {
      $value = $entity->getElementData($main_element_name);
      $referenced_mappings = $this->mappedObjectStorage->loadByDrupal($webform_element['#target_type'], $value);
      if (!empty($referenced_mappings)) {
        $mapping = reset($referenced_mappings);
        return $mapping->sfid();
    catch (\Exception $e) {
      return NULL;

In this case we are finding the entity referred to in the webform submission, loading any mapping objects that may exist for that entity, and returning the Salesforce ID of the mapped object if it exists.  Yours will likely need to do something very different.

There are actually two related functions defined by the plugin interface, defined in the base class, and available for override as needed for setting pull and push values independently:

   * An extension of ::value, ::pushValue does some basic type-checking and
   * validation against Salesforce field types to protect against basic data
   * errors.
   * @param \Drupal\Core\Entity\EntityInterface $entity
   * @param \Drupal\salesforce_mapping\Entity\SalesforceMappingInterface $mapping
   * @return mixed
  public function pushValue(EntityInterface $entity, SalesforceMappingInterface $mapping);
   * An extension of ::value, ::pullValue does some basic type-checking and
   * validation against Drupal field types to protect against basic data
   * errors.
   * @param \Drupal\salesforce\SObject $sf_object
   * @param \Drupal\Core\Entity\EntityInterface $entity
   * @param \Drupal\salesforce_mapping\Entity\SalesforceMappingInterface $mapping
   * @return mixed
  public function pullValue(SObject $sf_object, EntityInterface $entity, SalesforceMappingInterface $mapping);

But be careful overriding them directly. The base class provides some useful handling of various data types that need massaging between Drupal and Salesforce, you may lose that if you aren’t careful. I encourage you to look at the details of both pushValue and pullValue before working on those.

Okay, with the configuration and values handled, we just need to deal with programmatically telling Drupal when it can pull and push these fields. Most of the time you don’t need to do this, but you can simplify some of the processing by overriding pull() and push() to make sure the have the right response hard coded instead of derived from other sources. In this case pulling the field would be bad, so we block that:

   * {@inheritdoc}
  public function pull() {
    return FALSE;

Also, we only want this mapping to appear as an option if the site has the webform module enabled. Without it there is no point in offering it at all. The plugin interface provides a function called isAllowed() for this purpose:

   * {@inheritdoc}
  public static function isAllowed(SalesforceMappingInterface $mapping) {
    return \Drupal::service('module_handler')->moduleExists('webform');

You can also use that function to limit a field even more tightly based on the mapping itself.

To further ensure the configuration of this mapping entity defines its dependencies correctly we can define additional dependencies in getDependencies(). Again here we are tied to the Webform module and we should enforce that during and config exports:

   * {@inheritdoc}
  public function getDependencies(SalesforceMappingInterface $mapping) {
    return ['module' => ['webform']];

And that is about it.  Once the class exists and is properly setup, all you need to do is rebuild the caches and you should see your new mapping field as an option on your Salesforce mapping objects (at least when isAllowed() is returning true).

Sep 29 2015
Sep 29

While I couldn’t make it to Drupalcon Barcelona last week, I insisted on giving a presentation somewhere. Forum One let me do it as a webinar: Planning and Building Salesforce Integration. There are some truly wonderful tools for integrating Salesforce with Drupal out there, but the tricky part is planning, documenting, and estimating the task.

My webinar presentation doesn’t cover the basics of Salesforce or Drupal. Rather, it is tailored for a certain (basic) level of knowledge about both systems: what they are, how to set them up, and the basic data models. It covers the toolset you need, its strengths and weaknesses, how to plan an integration, and what kind of amazing cool stuff you can do if you’re smart about it.

[embedded content]

Salesforce is an extremely powerful CRM platform, and that includes options for external integrations. There are two different APIs available, but basically all you need to do is wrap requests in an OAuth2 session, and query the Salesforce data with their own query language, SOQL.

That said, I would never send you off to go and write totally custom integration code. There’s already a fantastic suite of modules written for Drupal that provide this base functionality and more: the Salesforce Suite. The Suite is actually a set of 5 modules, providing an OAuth2 wrapper and SOQL query builder, two different ways to connect to Salesforce, an interface to map Salesforce objects to Drupal objects, and separate modules for both pushing data into Salesforce, and pulling data from it. It’s a pretty complete package, and it is an excellent base for even highly-customized integrations.

Out of the box, the Salesforce Suite also does an excellent job of direct, 1:1 mappings between Salesforce Objects and Drupal entities. It schedules and queues synchronizations well, and offers a lot of hooks to help with your custom development. When you’re planning your integration, you have to look out for “red flags” that will help you estimate:

  • Conceptual objects in Drupal that are actually made of two or more Entities, e.g., users with their profile2 objects.
  • Modules that don’t store their data in entities, e.g., the Webform module.
  • A wide variety of different types of fields. Many fields actually need some translation between Drupal and Salesforce, and you will have to provide that yourself.

Very often, custom code is the best way to augment the synchronization that you’ve built in the Drupal UI. The following are some of my favorite examples:

  • Salesforce stores country names in a non-ISO format. If you’re syncing to a Drupal location field, you will have to do a little bit of translating.
  • Salesforce booleans (checkboxes) are stored differently from Drupal booleans. Again, a little bit of translation is required.
  • Salesforce doesn’t have the same validation rules as Drupal, e.g., Salesforce Contacts are not required to have an email address, but Drupal users are.

All this by way of saying that custom code is almost always required for an integration to go smoothly. It doesn’t have to be complicated code, but it is there.

With all this in mind, I recommend planning your integration around use cases and user stories for both systems. We paint a picture of the people who will be using both the Drupal and Salesforce sides of this, and what information they want to have readily available. In most cases it boils down to a long list of objects and fields to integrate. You can use this list to estimate based on the number of different field types, number of 1:many object mappings, etc. You can also use it to consider large-scale architectural options, like the decision to install the Redhen Drupal CRM to map all your data into these entities, and then sync from there.

Another important question (brought up by a participant during the live broadcast) is whether to keep your data customizations in custom Salesforce fields, or in Drupal custom code. Salesforce allows you to create custom fields that are filled automatically according to rules that you configure. So that country name problem I mentioned above could be solved by syncing Drupal with a custom Salesforce field that translates the names for you. That saves you the trouble of coding in Drupal, which otherwise seems like it would be hard to maintain. As a general policy I prefer to keep “code-like” customizations in the site’s codebase, where I can then easily find them in case of a problem or change. It can save serious headaches down the road! For those of you who are more comfortable in Salesforce, however, the same reasoning might push you towards the opposite decision. You should take the route that seems the most maintainable to you, and not worry about what some guy on the Internet said in a blog post ????

The webinar also includes a quick walkthrough of a simple integration between Drupal and Salesforce, synchronizing users and contacts. We right away run into some of those very predictable problems that our estimation process was designed to root out, and we talk about how to solve them.

My favorite part of the presentation (if I do say so myself!) is the “blue sky” section near the end. Salesforce and Drupal are each incredibly powerful, flexible platforms on their own, and so I really enjoy coming up with cool ways to have them magnify each others’ power. Some fun ones:

  • If you include Salesforce data in Drupal user objects, you can do A/B testing in Drupal based on peoples’ engagement in other channels.
  • You can tag users in your analytics package based on their Salesforce profiles. Imagine a special analytics report of how your donors behave on the site, or simply people who opened the last newsletter, or whatever other user group you can imagine.
  • You can track user engagements from your website in Salesforce. For example, including a user’s comments on blog posts as a part of their Salesforce history.
  • If your site is a user community with points, referrals, and other rewards for interaction, those should definitely be reflected in Salesforce profiles.
  • (My #1 favorite) You can integrate information from social media, email blasts, human contact, and the website to provide little surprises and personalizations for users. Imagine a message like, “we saw you shared our blog post on Facebook – thanks!,” or, “have you checked out the newest version of our product yet?”

We had some great questions come up in the Q&A section, as well. We talked a bit about using machine learning (and Salesforce’s new Predictive Decisions feature) to drive website content decisions, and potential future areas for development in the Salesforce suite around the Salesforce metadata API. Cool stuff, but you’ll have to watch the video to get it.

The Salesforce suite offers an excellent foundation for building Salesforce integrations. As always though, the really exciting stuff is on the cutting edge of what the industry offers. To take advantage of that, you’ll need more than just a small custom module, and you’ll also need serious strategic and developer leadership. I’m looking forward to having that conversation with you soon!

Previous Post

Coding vs Clicking and Building Layouts from 7 to 8 – More Fun at DrupalCon Barcelona

Dec 10 2014
Dec 10

I love Drupal and end up undertaking most of my programming projects with it. I have been using it for so long that I find it far easier to push out projects with Drupal than with anything else, despite it’s infamous learning curve.

Whether you want to call Drupal a CMS (Content Management System), a CMF (Content Management Framework) or a CMSomething, the ‘C’ always stands for Content. Content is where Drupal shines and is what it’s designed for.


When an organisation is at a stage and mindset that they also want to manage their contacts and interactions effectively they will often need tools designed specifically for that function. These are generally referred to as a CRM, which stands for Client Relationship Manager or Constituent Relationship Manager, depending on the sector (For-Profit or Not-for-Profit respectively). CRMs are big business, with many free and paid options available, all with their own advantages and disadvantages.

Often these interactions that people have with your organisation will include things such as registering for an event, making a donation, becoming a member, expressing interest in a product or receiving a newsletter. This all sounds quite simple, but often representing a business rule in the digital realm is very difficult as everyone thinks ‘their way’ is ‘the only way’ and that surely every off-the-shelf system should represent them out of the box.

What has a CRM got to do with Drupal? Nothing directly, but indirectly if you’re looking to streamline your business operations and automate the ways people can interact with you, your CRM will need to work well with your website.

If you are reading this, I will assume your website is likely built in Drupal and, unsurprisingly, Drupal continues its talents of playing well with others into the domain of CRMs.

In this article, we will look at several of the big players in the CRM space that work well with Drupal, how they integrate or how developers can get them to integrate.

Roll it yourself

Whilst I mentioned above that Drupal is aimed at Content, Drupal has always excelled at relationships between content, or in newer parlance, ‘Entity References’. Theoretically you could create content types for contacts (or the User entity) and content types for the interactions they may have with you alongside other modules such as Event and Commerce. Then create a bunch of Views and you effectively have a ‘CRM’. In some simpler cases this may be enough and you have complete control of the processes you want to represent. This could end up consuming a lot of Developer time and using a tool designed specifically for the job may be a better solution.


The team at ThinkShout effectively followed the advice above and created a collection of modules that will provide you with a lot of Drupal-Native CRM functionality. It takes the Drupal Commerce approach and instead of providing you functionality our of the box, rather provides you with a toolkit for creating that functionality. This does mean you may still need to undertake a lot of extra work yourself but you are adding no new systems into your technology stack and the problems that can entail. The other downside of RedHen is that it isn’t updated that regularly (in comparison with other options on this list), its release cycle is based more upon when ThinkShout have client requirements or run a code sprint.

If you want to customize RedHen then you can create your own sets of modules and theme overrides, it’s Drupal!


Often when searching for CRM options for Drupal, people come across CiviCRM and it initially appears as a Drupal module. I have undertaken a lot of work with CiviCRM (and contribute to the project) and the fact that it masquerades as a Drupal module is often a massive point of confusion. It’s best thought of what it actually is, an external service that happens to integrate quite well with Drupal and almost look like it’s a part of it. CiviCRM is a custom PHP application that also integrates with Joomla! and WordPress and is primarily aimed at not-for-profits but can be customized to work with commercial organisations. Out of the box CiviCRM comes with a LOT of features and is thus quite a weighty program, as an open-source project it can sometimes be a bit rough around the edges and inconsistent, but development has increased and matured a lot in the past year.

CiviCRM integrates directly with Drupal in many places, some better than others, and these include:

  • A direct (optional) relationship between a Drupal user and a contact record as well as synchronization between Drupal roles and CiviCRM groups and memberships.
  • Views for constructing listings of CiviCRM entities.
  • Rules and triggers
  • Webform for replacing CiviCRM’s inbuilt forms.
  • Commerce and Ubercart
  • Organic Groups
  • Theming (sort of). You can use the Drupal theme to override CiviCRM CSS but CiviCRM does use its own templating engine (currently smarty but likely switching to twig).
  • Another option is to set aside the Drupal/CiviCRM modules and utilise CiviCRM in a ‘headless’ approach, utilizing it’s API and REST interfaces.

CiviCRM offers a plethora of its own inbuilt customization options that I won’t go into, which, being PHP, will be reasonably familiar to many of you. You can also utilize direct CiviCRM to Drupal customization through creating your own Drupal module. In the module you can access CiviCRM hooks and API functions which offers an endless level of options to suit a plethora of client needs.


Salesforce is the main player for commercial sales focused companies and is reasonably entrenched in many business workflows. It is a large application with its own long established ecosystem and extension marketplace. It is expensive, proprietary and written in its own programming language, ‘Apex’. Development moves fast and it is a reasonably stable system.

Salesforce is a cloud based CRM, so integration happens via a REST interface. Fortunately as Salesforce is so popular in the types of environments that Drupal is also popular, there are a few prebuilt options. Bear in mind that your license may limit the options available to you:

  • The SalesForce Suite is a module with a long history and it starts you on your integration journey. It will handle authentication, mapping Drupal entities to SalesForce fields, pushing and pulling data between Drupal and SalesForce and a legacy module to connect with SalesForce’s older SOAP API.
  • There is some basic Webform integration via a module with seemingly stalled development.
  • Springboard is a commercial Drupal distribution from Jackson River that bundles Drupal and SalesForce together into an offering specifically for not-for-profits. It’s a little unclear what it offers above just doing integration yourself, but it includes support. There are also a couple of other agencies that offer Drupal / SalesForce integration services.

Theoretically, once you have SalesForce entries represented as Drupal entities you can utilize the plethora of customization options that Drupal offers such as views, rules and module hooks. If you want to enhance SalesForce functionality then it could potentially be accomplished through Drupal and pushed to SalesForce, but chances are you will need to learn SalesForce itself at some point.

Sugar CRM

Sugar is another long established player and comes in community (open source PHP) and commercial editions. It is aimed at commercial sales focused organisations and much like SalesForce, development moves quickly and Sugar CRM also has its own marketplace for extensions.

OSSCube created a Drupal module and accompanying SugarCRM project to allow for some direct integration. I haven’t personally used them and find the documentation somewhat hard to understand. However, it claims to offer:

  • Field mapping between Drupal and Sugar entities
  • Data syncing between the two systems
  • Webform integration to create Sugar entities

Again, you also have the option of utilizing Sugar’s SOAP and REST APIs to create Drupal entities and continue from there. Sugar also offers LDAP, so there is the potential to create a single sign on between the two systems.

Other Options

I have grouped together several other options here. All the CRMs vary wildly, but their integration options are mostly the same so I didn’t want to just repeat those options over and over again. I have attempted to undertake Drupal integration work with some of them in the past and often struggled to accomplish anything. A lot of this is due to the proprietary nature of these systems, finding information on their API and developer options is a challenging endeavor unless you pay some money.

Microsoft Dynamics and BlackBaud

It is worth including Dynamics and Blackbaud as they are big players in the CRM space. Both offer their own options for a CMS (and other systems), so are often reluctant to offer integration options outside of their ecosystem.

Nation Builder and Salsa

These are both CRMs of a sort that are aimed more at the newer wave of engagement focused organisations. They are not just about tracking contributions and interactions, but also encouraging interactions.

Salsa has some integration modules for Drupal 6, so if you’re feeling generous you may want to update them. Otherwise it’s a process of communicating via the Salsa API and fortunately there is a Drupal module under development to get you started.

NationBuilder have gone out of their way to not create any direct CMS integrations, citing that their inbuilt CMS is a far better option instead. However, they do have an API that can be used with the options noted below.

How to integrate with Drupal?

Here are some potential options, but your mileage may vary:

  • Utilize the CRM’s API (if it exists in your version / license) to pull entities into Drupal and then proceed in a similar vein to the SalesForce options via Services or a custom module.
  • Using the Migrate module and optionally the MSSQL Server PHP extension for any .NET based systems to directly query the CRM database and import into Drupal entities. Again, your license and setup may or may not allow this.
  • There is a Sandbox module for Dynamics integration, last time I tried, it didn’t work anymore, but you may feel like updating it.


This is by no means a comprehensive list of CRMs or the possible options to integrate them with Drupal. I have mainly focused on the ones I have experience with and can’t possibly keep up with all the new options that are constantly emerging. I also have far more experience working with not-for-profits, so my solutions are somewhat skewed to that sector. What are some of the systems you have tried to connect with Drupal? Did you succeed? Have you had to overcome any hurdles? Let us know!

Aug 17 2011
Aug 17

For us, every project starts with goals. From goals comes strategy, from strategy comes planning (information architecture, interaction design, technical architecture) and from planning comes (a rather agile) implementation.

And for implementation, 2011 so far is the year of Drupal 7 — at least it has been for us, because all of the projects we've started this year have implementation powered by Drupal 7 at the core. In some ways, we love Drupal 7 so much more than Drupal 6, we don't like to look back. It's almost painful to have to deal with Drupal 6 anymore … especially when it comes to theming.

Of course, this means we've had to do quite a bit of wrangling with module bugs, helping with upgrade-related issues, and contributing our own upgrades. Despite the success of the #D7CX initiative, the first several months of Drupal 7 contrib felt like life on the bleeding edge. That's only natural for such a large and robust system as Drupal has become.

Some of the modules we upgraded to Drupal 7 include:

SalesForce module and other modules
Our view is that the best way to upgrade existing Drupal modules is to contribute the work back to their respective Drupal.org projects. This way the client benefits from having more code supported by the Drupal community. We benefit by having extra eyeballs and keyboards working the challenges, as well as ensuring that the client's project remains on the open source path.

[Ported to Drupal 7, still in active development.]

With a project making very heavy use of quite deep Salesforce integration with Drupal, we had to get the Salesforce Suite into Drupal 7 shape. We've made quite a lot of progress. Yet this is a huge undertaking, and much is yet to be done. Check out the issue queue. For a sense of what's next and priorities, see the Salesforce Suite for Drupal 7 roadmap. (We'll have more on Salesforce integration with Drupal 7 in another post.)

[Available now for Drupal 7. Seeking (co-)maintainer(s).]

You may know it as "Your Dashboard" on Drupal.org. It was available in Drupal 6, but we needed it for Drupal 7, so we did the upgrade to a minimum viable product release. Homebox is available for your Drupal 7 project!

We don't have an internal need to push it further, but Homebox has tons of potential, especially for site managers and administrators. If you're interested in extending and improving Homebox, please share your patches, or even help review patches already submitted. Maybe you'd even like to be co-maintainer?

[Available now for Drupal 7. Seeking (co-)maintainer(s).]

Need a block to display stock prices? We ported the module from Drupal 6 to 7. It's still a -dev release, but fairly stable at the moment. At this point, we don't need to take it further. Future maintainer(s) interested in building out a cool set of stock-market-monitoring features are welcome to take up the baton from here.

[Available now for Drupal 7.]

Goodness knows this module faces a huge rearchitecting for Drupal 7, but we needed something working right away, so we've been working with maintainer Wim Leers to get a minimum viable product released. There's a lot more that could be done for HS, but at least it's available for Drupal 7 now, and I'm sure Wim would appreciate more loving community attention on this incredibly useful usability enhancer.

Oops, that's a cul-de-sac

One project we did that is now pretty much EOL is Apache Solr Pages. We needed the functionality for a project at the time, and Solr did not have it and there was no clear roadmap as to when or even whether it would have it, so we contributed this simple project. Now Solr itself provides that functionality, so this project turned out to be not needed.

The commons

In fighting the bleeding edge, we discovered a bug in, and rolled a patch to fix, the Aggregator module in Drupal core. (The patch was then adapted/modified by other community contributors to work on Drupal 8 first, for subsequent backporting. As I write this, it's ready to be committed, so it will hopefully be in a Drupal 7 point release very soon. Isn't open source great?) We also offered some small help in the issue queues on a number of contrib projects. It took a lot of work from this great community to bring Drupal to where it is now. We're glad to do our own small part.

That's the way of the commons. We consider it our responsibility to our clients to fix and update modules this way. The whole idea of using open source is that you adopt code that is shared by the worldwide community. If you don't share the fixes, not only is the community diminished as a result, you've also created your own fork that you, or your client, will be stuck maintaining alone. That's what we would call a bad strategy. Share in the commons and everyone benefits — the community, your client, you.

D7 to get you through these dog days

Every time a new major Drupal release hits the interwebs, there's a bit of debate over when that new release is ready for production use by site builders and owners, and it almost always comes down to the state of contrib. This summer, Drupal 7 contrib has really started to stabilize, with solid releases to many of the popular and useful modules out there. From our perspective, it would take some huge convincing to get us to consider Drupal 6 now for any new project, because Drupal 7 is coming of age and ready for its day in the sun. Not only that, compared with Drupal 6, Drupal 7 will have 1-3 years' more community support (barring an unexpectedly short Drupal 9 release cycle).

With such a large Drupal community, and the amazing proliferation of new module and theme projects, thanks to Drupal.org's new Git footing and the easy ability to create project sandboxes, it can be challenging to keep up with what's happening, or to even be aware of things that may be an awesome fit for your needs. There's simply too much Drupal community development activity to keep track of these days. This post is a little highlight of things we've been working on that you may find interesting, useful or even worthy of your support. I hope other shops, freelancers and hobbyists post about their work as well. It's a busy Drupal world. Shine some light on what you're doing!


15.81 KB f0fba1481fb8b3dfe46c63d456ff81e9 16.91 KB 846f9ac41601e4b613494635f6bd76e8

Last updated: September 13, 2011 - 00:48

Development snapshot from branch: 7.x-1.x

Release notes

Nightly snapshots of the D7 development branch.

Sep 01 2009
Sep 01

For a recent Drupal 6 project, we used Webform (http://drupal.org/project/webform), Salesforce (http://drupal.org/project/salesforce), and Salesforce Webform Integration (http://drupal.org/project/sf_webform) modules to create a custom form to send user data to Salesforce.com. Since the form was too long, we used the pagebreak component in webform to break the form into multiple pages. This not only improves the visual effects of a long form but it's also more clear when the form is made up of different sections.

read more

Aug 04 2009
Aug 04

Currently, we are working on a Drupal 6 project that requires integration with Salesforce.com. We use the Salesforce module for Drupal 6 (http://drupal.org/project/salesforce). We also use the webform module to create the desired form. The data will be sent to Salesforce.com after a user fills out and submits the form.

read more

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web