ThinkShout

We provide web strategy and open source technology to forward-thinking organizations.

MailChimp 2.0: Anatomy of a Drupal module rewrite

June 30, 2011 - 13:23 -- Lev

FreddieI first wrote the MailChimp module for a side project I was working on (MomHub) towards the end of 2007. It was my first standalone module, and the first one I posted on drupal.org. That module, and every update since, has basically offered the ability to synchronize a site's users with more one or more MailChimp lists based on role, in addition to standalone subscription forms. Over the years, the module has grown in popularity along with the MailChimp service itself, and there are now nearly 4000 reported installs. Still modest, but a sizable base which needs to be taken into consideration when releasing updates.

I quickly faced a challenge many contributors encounter as their modules grow in terms of usage and complexity. These challenges include finding the time to maintain and support the module and adding new features when there is no support from a client or employer. In this case, MailChimp came to the rescue and partnered with ThinkShout in late 2010 to sponsor the module. We agreed to maintain and support the current Drupal 6 version and rewrite the module for Drupal 7, for which we have just completed a release candidate.

Why a rewrite

  • Nearly 4 years of technical debt, time to start clean.
  • Initial architectural decisions could have been better. It was my first module!
  • Take full advantage of changes in the MailChimp API.
  • New features and API changes in Drupal 7, which we've used whenever possible:
    1. Entities
    2. Tokens
    3. Form states
    4. Queue system for batch processing during cron
    5. Mail interface
    6. Custom cache bins

What's new

Modular architecture

The origin module had all of the features crammed into a single module, with a focus on meeting the needs of a very specific feature set. The result was features that were difficult to extend / alter, and almost no API interfaces for developers to interact with the MailChimp API outside of the fairly narrow use case the module handled. For 2.0, we split out the module into a fairly minimalist API module, with a submodule for each primary use case. In addition to providing much needed flexibility, this makes the module much more lightweight as submodules can be enabled/disabled as needed, or just the API module can be used for custom development. The module is currently bundled with the following:

  1. mailchimp.module: Core API integration and general settings.
  2. mailchimp_lists.module: This contains much of the functionality of the original module, namely synchronizing Drupal users with MailChimp lists and enabling subscriptions, albeit with many improvements.
  3. mailchimp_sts.module: Allows all site emails to be sent through the MailChimp STS API by exposing it as a Drupal mail interface.

As significant new features are added, the additions will be as new submodules, or standalone projects dependent on just mailchimp.module.

Lists as first class citizens, err, entities

In MailChimp 2.0, lists are entities, enabling all the entity goodness that Drupal 7 provides. The previous version simply saved each MailChimp list and its corresponding settings into a single serialized variable. There are several reasons this approach could be improved upon, not the least of which it forced a 1 to 1 correlation between MailChimp lists and their use on the site. Now, you can essentially create more than one instance of a MailChimp list, assigning it unique meta data, exposing it as block, etc.

Since lists are entities, they are also field-able, allowing for customizations during enrollment, Views and Rules integration, you name it! After much deliberation, we decided to base the entity structure on the EntityAPI, primarily to gain the Views and Rules integration essentially for free. Hat tip to everyone involved with that project.

Revamped user interface

One of the major problems the module faced was a cluttered interface, especially when an organization had more than a few MailChimp lists. First off, all of the settings for the module and for each individual list were smashed into a single page, with nested collapsible fieldset hell. It seemed like a good and easy solution when first conceived, but fell apart as the number of settings grew, and completely failed to account for an organization having many lists. The approach we took was to first break out the list settings into their own tab / local task. The list landing page simply has a table of existing lists with summary information and links to edit the list and directly access the associated MailChimp list. There's also a local action for adding a new list. The list add/edit form itself lives on its own and is complex in its own right, especially with all the new features and settings. It still has a few of those fieldsets, but they are used to categorize the sections of settings. We're also taking advantage of form states to toggle the available settings based on the type of list selected. There's an ajax callback that occurs to get the available merge fields for the selected MailChimp list so they can be mapped to available user tokens. Below are some before and after screenshots for comparison.

Before

After

Performance, caching, and webhooks

The other single largest problem was performance. The module made way too many requests to the MailChimp API, and on the rare occasion, when the API was slow to respond or down, the corresponding Drupal site would also be affected. The two main situations the module was communicating with the API was to get a fully loaded list object and to get a users information for a given list. E.g, when subscription block was rendered, it would reach out to the API to get the list details and to determine if a user was subscribed to that list. Further, it would make a separate call to get the interest groups for the list. Caching to the rescue!

The main method to get information about a list, mailchimp_get_lists($list_ids = array(), $reset = FALSE), will now store list objects received from MailChimp in the default cache bin with a CACHE_TEMPORARY lifetime. That cache is flushed on request, when the $reset parameter is set to TRUE, or when the site-wide cache is cleared.

More importantly, the module adds a custom cache bin for mailchimp_users which stores the memberinfo object for each email address / MailChimp list combination. This is filled upon the first request, E.g., the first time a subscription form is shown for a given user / list, and is only cleared in the following situations:

  1. A user updates their subscription through the Drupal site.
  2. A user updates their subscription outside of the Drupal site and a MailChimp webhook fires refreshing the cache. This is an optional setting available with each list.
  3. A developer forces the cache reset.

With this architecture, requests to the MailChimp API are kept to a minimum while maintaining a fresh cache of all essential information on both MailChimp lists and user information.

MailChimp STS

Aside from the mentioned improvements, the major new feature included with this release is integration with the MailChimp STS API. STS is essentially a wrapper around Amazon's SES with, according to MailChimp's launch announcement, these additional features:

  • Slightly simpler API
  • Opens and click tracking
  • Visual reports (all pretty and stuff) dashboard to see stats
  • Keep more than 2 weeks of history, showing hourly stats for at least 6 months.
  • You can tag your transactional messages with custom labels (up to 100), and see your stats filtered by tag (say, one for receipts and one for confirmation messages)
  • Automate some of the SES limitations – if you exceed your daily or hourly quota by a small amount, we’ll queue the message for you and try to send it later. If you send from an address you haven’t verified, we’ll again queue the message and automatically send the verification request

Drupal 7 has an elegant mechanism allowing sites to alter how emails are sent by implementing an instance of the MailSystemInterface and setting a global variable indicating that it should be used. The module does this, passing the mail key as a tag to the STS send method so that site owners can view detailed reports for each type of email sent on their site. Report segments are hourly and include tag, number sent, bounces, rejects, complaints, opens, and clicks.

Roadmap

While this is a major milestone for integration between MailChimp and Drupal, we have lots of exciting additional features planned, in addition to what I'm sure will be plenty of refinement and issue resolution as usage of the new version grows. Pending feedback from the community, we're going to work on:

  1. Campaign creation from within Drupal.
  2. E-commerce 360 integration
  3. Analytics 360 integration
  4. Importing user level campaign data
  5. Dynamic list creation based on select criteria
  6. More granular control over when to use STS

We'd love to hear from you, both your feedback on the new module and thoughts on the roadmap.

Comments

Submitted by Ben K. (not verified) on

Wow, this sounds really, really awesome! Can't wait to test it out.... :-)

--Ben

P.S. I'd love to have you come present your new work to the Portland DUG if you're up for it.

Submitted by Lev on

Thanks Ben, I'd love to hear your thoughts on the new version. You've been a big supporter of this module all along. And certainly, I'd be happy to share at an upcoming PDUG meeting.

Very nice writeup!

I'd highly recommend checking out the vertical tabs element provided by Drupal 7 core for some of your forms The forms at http://thinkshout.com/sites/default/files/mailchimp_6_settings.png and http://thinkshout.com/sites/default/files/mailchimp_2_0_addlist.png could really be polished and cleaned up. They are using lots of gratuitous fieldsets. Just be sure to read the UI guidelines on how to properly use them: http://drupal.org/node/1087100.

Keep up the good work!

Submitted by Lev on

Thanks for the feedback Dave, much appreciated! I had actually initially built the list form using vertical tabs but at the last minute removed them for a couple of reasons.

* The choices in the settings fieldset change quite a bit depending on the type of list selected. When that fieldset was in a vertical tab, it wasn't obvious to the user that the choices were setting, at least unless I made the default tab. I also wanted the varying settings right next to the field that was triggering the change.
* When changing the MailChimp list, the form does an ajax post back to the get the available merge fields. I was having trouble getting the results to correctly populate a vertical tab.

If you have any tips, I'd be happy to give it another go. Overall, I do appreciate the UX improvements vertical tabs provide.

Oh, and I did find a bug related to collapsible fieldsets in Seven. If a parent fieldset is collapsible, and it contains a nested fieldset that is not, the down arrows still appear. I'll post on d.o.

Submitted by heyrocker (not verified) on

It is fantastic to see MailChimp come forward and sponsor this development, a real synergy where everyone wins. I know we use this module a lot at NodeOne, looking forward to working with the new version!

Submitted by Lev on

Yeah, MailChimp has been amazingly supportive and really great to work with. And I'm not just saying that :) We've reviewed the module with one of their engineers, they're going to write a press release to promote it, and they've recently launched an integrations directory, which lists the module.

Submitted by Lev on

It does using the list type "free form". This list type presents all the MailChimp merge fields in a subscription form. It's exposed as a block, and is available, along with all the lists a user has access to, at /mailchimp/subscribe. That path is a suggested menu item.

Submitted by bitkid (not verified) on

How do you plan on doing the campaign creation? I mean, creation is easy with the API but what about the content? Are you just going to provide a text area where users can enter html or what is your plan?

Submitted by Lev on

Not sure yet, but likely some combination of node content, maybe Views, and maybe just a text area. We don't want to recreate the MailChimp campaign creation interface within Drupal, just focus on what's unique within the Drupal environment.

Submitted by bitkid (not verified) on

The reason I was asking, was because I have made a module for that.

I'm working for a newspaper (till the end of next week, then I get to work with the great folks at NodeOne).

We send a newsletter every day (at 11:15 local time), containing the articles that are newer than the one we sent yesterday.

When I started this project I took a look at your code and used whatever I could, which was not much, since we decided to not have the recipients in our own database. But the api-key part in the admin section was useful. :-)

Here is what I did.

I created a module that needs:

1. A content type called newsletter, that contains the delivery timestamp, the status (not ready, ready and sent) and a date range. The date range is used to limit the newsletter to only contain content (articles, galleries, blogposts etc.) that is published within this time frame. It also contains a newsletter type reference which is:

2. A taxonomy term called Newsletter type, that is used for the different types of newsletters. This could be "Daily newsletter" and "Weekly roundup" etc. It is in the term you enter the listid of the receiver list.

3. A View that gets the content. It also contains a date range that uses fictive dates.

4. A theme containing the newsletter design.

The module makes sure that /node/[nid] shows the newsletter node and that /newsletter/[nid] shows the view where it changes the date range to match the date range from the newsletter node with the same [nid].

A hook_custom_theme() changes the theme on the /newsletter/[nid] page. (that is actually implemented in a different module, to keeps this module as generic as possible).

I use draggableViews to prioritize the items in the view.

In the newsletter node a subject can be defined, but if it is not, the subject is automatically created using the title of the first 3 items in the draggableview.

On every cron run my send function is called (using hook_cron_queue_info() and hook_cron()).

If the send function finds newsletter nodes that is set to status 'ready' and has a delivery date now or in the past, it creates a new campaign at mailchimp, gives it the url to the view and the listid to send the newsletter to. Finally it asks mailchimp to send the newsletter.

This of course will not fit everyone, but it works great for our business in the media industry.

What still needs to be done, is a script to auto generate newsletter nodes, so that we don't have to do that manually every day. Also a lot of stuff is not completed and a few things needs to made more generic, for others to use the module.

I am planing on open sourcing it later on when it is more complete.

I believe our modules can easily co-exist, your module dealing with recipients, mine with generated content.

So, what do you think of my way to generate content for a newsletter?

Submitted by Lev on

Hey bitkid - Starting a concerted effort this month on a MC Campaign module. Love some of the concepts you outline and happy to explore a collaboration. I'm leaning towards a simpler / more bare bones architecture that can be extended, namely allowing users to create and send campaigns by selecting one or more nodes into any region within a template pulled in from MailChimp or just filling in a text area and then sending and viewing reports. I'll also stub it out with generic API calls so it can be leveraged by other modules / devs. Let me know your thoughts on collaboration and where you're at with the module you wrote.

Thanks, Lev, for your work on this and this peek behind the curtains. My nonprofit was previously using Drupal 6 and CiviCRM's CiviMAIL but stopped because I felt the latter was lacking in features and performance. We're now happily using Mailchimp with Drupal 6 and looking forward to updating our site to 7. Do you see any opportunities for integrating Mailchimp with CiviCRM? While I didn't like how their mail module worked, I liked a lot of the rest and want to see if there's a way to have all our data connected and collected in one place. I've asked the same question within the Civi community but it seems to have fallen upon deaf ears.

Submitted by Lev on

Hi Roger - We don't have immediate plans to integrate with CiviCRM, although [you're not alone in wanting it](http://drupal.org/node/272044). It's really more of a CiviCRM plugin that'd be required, not a Drupal module. That actually points to one of the core issues with CiviCRM when used with Drupal, it's a complete separate application bolted on top of Drupal with its own APIs, architecture, etc. ThinkShout's exploring developing a native Drupal CRM, and we'd certainly include tight integration with MailChimp.

for that update. I'll be following your work in the hopes that the Drupal CRM takes shape. I suspect there's a large and eager audience/market for that. Thanks for all the work you do to put technology into responsible hands.

I have tried to adapt the module to our site on http://bilalzentrum.de but there is a problem when it comes to integration of the subscribers form.

The user fields that I have created (field_lname, field_fname) don't show up on the selections list when I want to configure mailchimp to use my existing database.

I recall that this has been working pre mailchimp_7.2 but now in the stable release, after I had to switch to free-form the fields don't show up in my selections list. http://bilalzentrum.de/mailchimp_selection.png
http://bilalzentrum.de/mailchimp_mergefields.png
here is the list of user fields:
http://bilalzentrum.de/user_fields.png

What did I miss, should I have put the fields somewhere else?

Apparently this is indeed working now, I reinstalled token module and things showed up as desired.
I don't know however, why it did not do so in the first place maybe some incompatibility with token7.x-1.0-beta2?
I am now working with 7.x-1.0-beta5 and things work fine, thanks for this great module.

Thanks so much for creating these D7 modules! I look forward to making them work on my Drupal 7 site.

Is there documentation for this module, other than this blog post and the page at http://drupal.org/documentation/modules/mailchimp ?

I have installed all three Mailchimp modules on a new Drupal 7.10 site, but have no idea what to do next. When I click "configure" for any of the three modules (on the Modules admin page) I see the same config screen for each, which has just my Mailchimp API, a dropdown for the batch limit, and a checkbox for secure connection to Mailchimp. That's all!

I can't find any indication of what to do next so that I'll see the rich config screens shown in this blog's screenshots.

Yours,
Doug

Submitted by Lev on

Each module has it's own fairly comprehensive README file with some detailed instructions. Best of luck!

Your not kidding when you say complex readme.txt files. How about a step by step tutorial or a online class or something? This is hard. So many questions about settings, etc. Or teach me and I will put one together.

I have exactly the same problem - how do you get to see the screenshots you show? After installing v2.6 I got functionality which I think was already there in 1.x - nowhere near the nice screenshots displayed here.

My main interest is that I want to create an RSS campaign with Mailchimp, and I want to control what is displayed in the email.

Thanks & cheers --Mike

Submitted by Sean on

There are over 10,000 Drupal websites using the MailChimp module - and therefore there's a great community of folks on Drupal.org who are happy to help you use this module. Please ask your questions regarding configuration there, in large part because the answers will be helpful to other users there.

Thanks!

Submitted by Thierry Poullain (not verified) on

Hi,
I read the readme file for drupal mailchimp module, and i was said :
## Using MailChimp Macros
This module includes a special text format called _MailChimp campaign_,
allowing the use of macros to insert Drupal content into campaigns.

### Format
`[mailchimp_campaign|entity_type=node|entity_id=1|view_mode=teaser]`

do you have more information about that? any exemple ?
I try to fill content from drupal (some view) into mailchimp template.

Regards

Submitted by Davide (not verified) on

I have the same problem.

In Global setting I have only "Mailchimp API Key" "Batch limit" and "Use Secure Connection" options.
In Mailchimp Sts read "There was a problem accessing the MailChimp STS service: Please setup the integration first"

thanks

I'm working on a project where I want to provide users of a certain role their own email list that they can use. I think this new Mailchimp module can be made to do what I want, but I want to make sure before diving in. First, can email lists be assigned to individual users? Can emails be sent to these lists by users from Drupal? What I am looking to do is have these users be able to write a blog post on the site, and trigger that post to send to their email list at the same time, in one step. I understand that each list would have to be set up in Mailchimp, which is OK since we are not looking to have a huge number of these users (under 100). But being able to someday create campaigns withing Drupal is a big plus.

I appreciate any guidance you can offer on this matter. Thanks.

Submitted by Lila (not verified) on

Good work! This module would be even better with more information and integration with Rules!

Submitted by Sean on

Thanks for the feedback. Do you mind elaborating on what Rules integration you'd like to see?

Our Mandrill module provides Rules integration, since it's a transactional email service. I'm not sure how Rules integration would work with opt-in newsletter signups with the MailChimp module - but we're happy to explore.

Submitted by Chris (not verified) on

I'd love to see rules integration with triggers for subscribe/unsubscribe events.

Also some flexibility around the subscription block would be awesome ie. redirect after submit (for seo tracking)

Submitted by David Chandra (not verified) on

Hi Lev,
I have a problem with my MailChimp. It shows "The MailChimp MCAPI library could not be found." in the reports. And my MailChimp is not loading MailChimp lists.
I have tried to find the solutions in drupal.org (http://drupal.org/node/1883984), but still not successful.
My Drupal's version is 7.14
My MailChimp module's version is The 7.x-2.10
Could you help me with this?

Submitted by Frerem (not verified) on

I had the same problem...
But i did not read about the update information.
"the MailChimp API library is no longer included with the module due to license incompatibility"

http://apidocs.mailchimp.com/api/downloads/mailchimp-api-class.zip
So it needs to be downloaded and placed in your Libraries folder.

Submitted by David Chandra (not verified) on

I did put the MCAPI file in the "/libraries/mailchimp/MCAPI.class.php", but still doesn't work.

Am I missing something?