newsletter emails
2016 30 Apr

User specific marketing emails using drupal queue and simplenews module

Ever faced a situation where you need to send mails to your subscribers with content they choose to subscribe to, in a single mail?

Well, that means if a subscriber has subscribed to multiple categories then the crux of those content related to only those categories will be sent to that subscriber and that too in just one mail.

If that is your situation then here we are going to handle this situation in a much easier way making use of ‘simplenews’ module. We will dig deep into drupal simplenews rules and the simplenews module.

However, simplenews alone will not be able to handle this situation on its own, we will need to tweak a little bit.

What simplenews does:

  • When we create a mailing list simplenews ultimately creates categories or taxonomies of newsletter vocabulary behind the scene.
  • When visitors subscribe to such mailing lists then their email ids are stored in a table named: "simplenews_subscriber" which is provided by simplenews module itself.
  • Also data such as the mapping between emails and mailing lists ie. which user is subscribed to which mailing list is also stored in a table named: "simplenews_subscription".
  • Now you have to create a node of type 'newsletter' which is also provided by simplenews, Admin > Content > Add content > Simplenews newsletter.
  • Add mail content in that node and then attach that node to one of your newsletter category, and set frequency of mail.
  • Now at each cron run simplenews puts each mail into another table named: "simplenews_mail_spool" and sends your newsletter node to those who are subscribed to that category same as that of node.

 

Huh, all of that manual process, this is actually the flow of simplenews.

But the situation I faced while working on newsletter task for a marketing site was something like this: I had to send newsletters which varied according to subscriber’s choice of category and grouped into latest, featured, most viewed tags. If you are also facing the same situation then below description will sort you out.


 

What we will do:

  • So we are going to automate this process of building newsletter, considering we want to send out dynamic content from the site.
  • Let's take my own case, my site had five different categories of blogs and thus a user can subscribe to multiple categories. So ultimately content of newsletter will vary for each subscriber.
  • So before starting up, make some mailing list or categories in simplenews (which ultimately creates taxonomies under newsletter vocabulary), you can make mailing lists here Admin > Configuration > Web Services > Newsletters > Add newsletter category.
  • Make a vocabulary, let's assume "blog_posts", as this was my case because I wanted blog  teasers in my newsletter mails.
  • Now add terms with the same name as that of newsletter category terms (as we are going to match terms of both blog_post and newsletter vocabulary later in this section).
  • Now tag your blogs nodes with taxonomy terms of blog_posts vocabulary which we created recently.
  • So we are actually using simplenews module to manage our subscribers, subscriptions and mailing lists, but creating mails is the task we would like to handle on our own so that we could put the desired content dynamically according to each subscriber's choice.
  • From now onwards we will put our code in a separate custom module, so make one for yourself.
  • Now we will process all the newsletter request in a cron job which will be triggered every 30th day (as I want to send newsletter monthly to my subscribers, you can set a different time period if you want to).

 

hook_cron() { /* do your job here. */ }

 

We will add all of our subscribers in a queue which will be processed at every cron run and if we get timed out we still hold our data in a queue saved which will get processed in the next cron run.

I run cron every day on my instance and so if you want to send newsletter monthly then add a check inside your hook_cron such that it check when was the last time cron ran, like as shown below:

 

/**
 * Implements hook_cron(). 
 */ 
function my_module_cron() { 
  $last_cron_run = variable_get('newsletter_cron_last_run_time', 0); 
  $current_time = time(); 
  $time_since_last_run = $current_time - $last_cron_run; 
  
  /* Calculate time elapsed since last month. */ 
  $time_last_month = strtotime('now - 30 days'); 
  $time_diff_since_last_month = $current_time - $time_last_month; 
  if ($time_since_last_run >= $time_diff_since_last_month) { 
    /* do your job here. */ 
  } 
}

 

We will make use of the following database tables of simplenews:

  • simplenews_category: holds newsletter categories which we created earlier.
  • simplenews_subscriber: holds subscribers email id, whether that subscriber is activated or not etc.
  • simplenews_subscription: holds subscription information, as which subscriber is subscribed to which mailing list.

Extract all of your subscribers which are currently active and add all results in a drupal queue.

So now our hook_cron will look like this:


 

/** 
 * Implements hook_cron(). 
 */
function my_module_cron() {
  $last_cron_run = variable_get('newsletter_cron_last_run_time', 0); 
  $current_time = time(); 
  $time_since_last_run = $current_time - $last_cron_run; 

  /* Calculate time elapsed since last month. */ 
  $time_last_month = strtotime('now - 30 days'); 
  $time_diff_since_last_month = $current_time - $time_last_month;
  if ($time_since_last_run >= $time_diff_since_last_month) { 
    $query = db_select('simplenews_subscriber', 'subs'); 
    $query->join('simplenews_subscription', 'scrip', 'subs.snid = scrip.snid'); 
    $query->fields('subs', array('snid', 'mail')) 
        ->condition('scrip.status', 1, '=') 
        ->condition('subs.activated', 1, '=') 
        ->distinct(); 
    $result = $query->execute(); 
    $record = $result->fetchAll(); 
    $queue = DrupalQueue::get('my_blogs_newsletters_queue'); 
    foreach ($record as $key => $subscriber) { 
      $snid = $subscriber->snid; 
      $queue->createItem($subscriber); 
    } 
  }
}

It's just halfway, so in our cron job, we are just adding all of our subscriber's email id and their associated subscriber nid (snid) to drupal queue.

Now we will implement hook_cron_queue_info() this hook is triggered at every cron.

This function is called for each queue entry, hence we will build and send our newsletter here for each subscriber separately.

You may notice that I have set my cron frequency such that it runs every day. So if somehow our queue is not processed completely because of a timeout, then the remaining queue will get processed later on the next day when the cron will run.

Note: Entry from the queue is deleted as soon as it is processed.

Let's name our queue: “my_blogs_newsletters_queue”.

You can find a table named “queue”‘ in the database where you’ll find your entries.

/** 
 * Implements hook_cron_queue_info(). 
 */ 
function my_module_cron_queue_info() { 
  $queues['my_blogs_newsletters_queue'] = array( 
    'worker callback' => 'my_blogs_newsletter_mail_sender', 
    'time' => 120, 
  ); 
  return $queues; 
}

 

I defined a 'worker callback', which will handle each queue entry separately.

This worker callback function is called for each entry in queue, with parameter as that entry stored in database.

 

function my_blogs_newsletter_mail_sender($subscriber) { 
  // snid and mail of subscriber.
  $snid = $subscriber->snid; 
  $mail = $subscriber->mail; 

  // Get all tid(s) of subscriber to which it is subscribed.
  $subscriber = simplenews_subscriber_load_by_mail($mail); 
  $tids = $subscriber->tids; 
  // Get all blog_posts taxonomy terms. So that we could map our newsletter 
  // terms with that of blog_posts terms.
  $blogs_category_tree = taxonomy_vocabulary_machine_name_load('blog_posts'); 
  $blogs_category = taxonomy_get_tree($blogs_category_tree->vid, 0, 1); 
  // Reset variables.
  $blogs_view_output = NULL; 
  // If subscriber is activated.
  $activated = $subscriber->activated; 
  if ($activated == 1) { 
    foreach ($tids as $tid_key => $tid) { 
      $newsletter_term_object = simplenews_category_load($tid); 
      $term_name = $newsletter_term_object->name; 
      // Compare each blogs_group category term with that of newsletter category.
      foreach ($blogs_category as $key => $blogs_category_terms_value) { 
        $blogs_category_name = $blogs_category_terms_value->name; 
        $blogs_category_tid = $blogs_category_terms_value->tid; 
        // Blogs views output according to blogs category.
        if ($term_name == $blogs_category_name) { 
          // Now this one is important, you can make your output of blogs views
          // and filter the results through contextual filters, send the 
          // parameter for contextual filter as a third param, as shown below.
          $blogs_view_output = views_get_view_result(
            'my_blogs_view_newsletter',
            'latest_feat_blog', 
            $blogs_category_tid
          ); 
        } 
      } 
    } 
  } 
  // Theme your mail by defining a custom theme and do your inline css, 
  // so that your mail look professional and beautiful.
  $variables['blogs_view_object'] = $blogs_view_output; 
  $body = theme('my_custom_theme', $variables); 
  $to = $subscriber->mail; 
  $from = 'no-reply@mysite.com'; 
  $subject = 'Your mail subject goes here'; 
  $headers = ''; 
  // Send mail by defining hook_mail for it.
  drupal_mail('my_module', 'my_mail_unique_key', $to, language_default(), array('body' => $body, 'subject' => $subject, 'headers' => array($headers)), $from, TRUE); 
  // Log that newsletter is sent to a recipient, 
  // you must know who got your mail, just for your information. 
  watchdog('blogs_newsletter', 'Blogs Newsletter sent to: ' . print_r($data, TRUE)); 
}

 

I used a custom template to construct by newsletter and then finally to theme your newsletter, it's better to use inline CSS instead of internal CSS as some mail services do not allow that (including gmail).

 

Hence your dynamically generated mail is sent to the user.

Post your comments and suggestions down below.

 

PS: There is also a module called “maillog”, which helped me a lot in debugging, it uses dpm function to display the email message whenever an email is sent out from your instance, well you can also apply a watchdog near that dpm in this module and “reroute_email” module which re-routes all mail to a specified email address. You can also go through the module called "simplenews scheduler", which sends a newsletter as a re-occurring item based on a schedule.

Latest Blogs

Smart Website Design Fuels Business Growth

How Smart Website Design Fuels Business Growth ?

No doubt! With many people using the internet for business and information, owning a website has become more important than ever.

Read More

Top Trends in iOS Development Languages

Top Trends in iOS Development Languages: What's Next?

iOS development is always an exciting field; however, with millions of applications in the Apple Store, it is reaching an all-time high.

Read More

how to design a website

How to Design a Website: 8 Steps to Creating an Effective Site Design

In this day and age, having a website for a business is crucial to succeed.

Read More

10 best practices for solid web application security

Web Application Security: Best Practices for a Solid Architecture

In a tech-evolving world, it is equally important to strategize the security of web applications! Are you looking for ways to do it?

Read More