Skip to main content

Blog Post

How to Avoid Sending Test Emails to Real Users

Have you ever accidentally triggered emails to real users while working in a development environment? Or how about accidentally pushed data to a "live" third party service from a development environment?

There are tools (such as the Reroute Email module) you can use to prevent these mistakes, but the trick is remembering to enable them after importing the latest production database! It would take more than one hand to count the number of times I've only just remembered to "neuter" my local environment before taking an action that could have affected live users or data. Scary!

I don't like that kind of anxiety, so I wrote a small custom module that provides some peace of mind. I called it the "<client>_environment" module, and now you may be thinking, "Wait, isn't there already an Environment contrib module on drupal.org?" Why yes, there is. It's a great tool for automatically managing modules (and taking any arbitrary action) on a per environment basis. However, it doesn't provide the security and peace of mind I needed to ensure a given module is enabled in all development environments, as it still requires the developer to run the "env-switch" drush command. Yes, this could be automated to happen after a call to sql-sync, but this isn't good enough, since I often use different methods for importing production data. Same goes for santizing user emails and other production data: Sanitization can be configured to happen automatically after sql-sync but cannot be guaranteed when the dump comes from other sources.

So far I've left out one major benefit: Not only does it protect me from making blunders with production data and emails, but it prevents everyone on the team from making them. As lead on a given project, this is invaluable peace of mind! I now have confidence that a newly-onramped team member can't accidentally affect production systems while getting familiar with the site.

A final note on the choice of implementation. I specifically needed my code to be included a module that ran on production. Otherwise, we're back to square one with needing to remember to enable a module after importing the database dump. I want it to happen automatically. So, as you'll see in the source code below, the module runs on production but does no work.

I used a hook_init() implementation to provide the desired safety net. Yes, it's a performance hit (runs on all but cached pages), but does very little work in the production environment. In addition to the code below, one step is needed: Set a $conf[] variable in the production site's settings.php as a flag for environment detection.

Here's an example of configuring Reroute Email to always protect development environments. Note that this example is slightly facetious, since Reroute Email can be enabled on production and configured to do work or not based on variables in settings.php $conf[]


/**
 * Implements hook_init().
 */
function mymodule_init() {
  // We try REALLY HARD to determine if we're in a production environment.
  //
  // Check various flag names in case someone got cheeky and changed the value.
  // This is the $conf[] variable set in production settings.php.
  switch (variable_get('mymodule_environment')) {
    case 'prod':
    case 'production':
    case 'live':
      return;
  }
  // In case someone deleted the $conf[] variable, check sites directory name
  // for a production-ish value. Again, check various names in case the sites
  // directory name changes in the future.
  switch (conf_path()) {
    case 'sites/prod':
    case 'sites/production':
    case 'sites/live':
      return;
  }
  // We are in a development environment! Please proceed.
  //
  // Is email rerouting enabled?
  if (!module_exists('reroute_email')) {
    // Enable email rerouting.
    if (!module_enable(array('reroute_email'))) {
      // We couldn't enable the module. Darn! Don't let them proceed.
      throw new Exception('The Reroute Email module could not be enabled. This'
        . ' module is a required dependency for all development environments.');
    }
    // Flush caches to make rerouting take effect after module enable.
    drupal_flush_all_caches();
  }
  // Make sure a valid Reroute Email address is defined. Because when it is left
  // empty (or is invalid), emails go to their original destinations.
  if (!valid_email_address(variable_get('reroute_email_address'))) {
    variable_set('reroute_email_address', 'admin@localhost.localdomain');
    // The user may have set an invalid email address in settings.php $conf,
    // in which case the above call to variable_set() did nothing.
    if (!valid_email_address(variable_get('reroute_email_address'))) {
      throw new Exception('You specified an invalid reroute_email destination address. Please check settings.php or the variables table.');
    }
  }
  // Remind the user about email rerouting.
  $message = t('All emails currently routed to <strong>@email</strong>. 
<a href="@link">Click here to change</a>.', array(
    '@email' => variable_get('reroute_email_address'),
    '@link'  => url('admin/config/development/reroute_email'),
  ));
  drupal_set_message($message, $type = 'status', $repeat = FALSE);
}

Additional Resources

Learning How To Install Drush on Non-Admin Rights Server | Mediacurrent Blog Post
A Better Access Denied Page with Panels | Mediacurrent Blog Post 

Access icon Up arrow icon Drupal 8 icon Facebook icon - white Facebook icon - blue outline Facebook icon - yellow Hollow right arrow icon Hollow right arrow icon - white LinkedIn icon - white LinkedIn icon - hollow LinkedIn icon - blue outline LinkedIn icon - yellow Mediacurrent wordmark Quote icon Twitter icon - white Twitter icon - hollow Twitter icon - blue outline Twitter icon - yellow Youtube icon - white Youtube icon - yellow