How to Override E-Mail Recipients in WordPress Development Environment

Styled word e-mail with an @ style instead of the a

Chances are, if you’re developing a web application, you’re going to have it send e-mail to users at some point in the application.  Be it a registration e-mail, a lost password notice, or any other notification that might come from your application.

While working in your development environment, you don’t want messages like these being delivered to your real users – but you still want to be able to test that these features work as planned. This is especially true if you have e-mail scheduled to be delivered based on something like a cron job, where no express action on your part is required for the messages to start flowing.

The method I’ve implemented in my client projects to avert this disaster is to hook into the wp_mail filter, and force every outgoing message from WordPress to be delivered to my e-mail address instead.

Step 1: Add a variable to your wp-config.php to determine which environment you’re in.

In my wp-config.php, I define a new constant that determines if I’m in my development, staging or production environment.

define('WP_ENV', 'dev');
define('WP_ENV', 'staging');
define('WP_ENV', 'production');

Uncomment the appropriate environment when setting up your configuration.

Step 2: Override the mail recipient

Add a new function to your site’s functionality plugin (or to your themes functions.php file if you’re not using a plugin for site functionality) that filters e-mail sent with wp_mail.

* Override outgoing e-mail messages if in development environment.
* Send messages to pre-defined e-mail addresses for testing.
add_filter( 'wp_mail', 'override_mail_recipient');
function override_mail_recipient ( $args ) {
    $to      = $args['to'];
    $html    = $args['html'];
    $subject = $args['subject'];
    $message = $args['message'];
     switch (WP_ENV) {
         case "dev":
             $subjet = '[DEV] ' . $subject;
             $to = get_option('admin_email');
             $message = 'DEVELOPMENT ENVIRONMENT.  THIS MESSAGE WOULD NORMALLY HAVE BEEN SENT TO: ' . $to . '\r\n' . $message;
             $html .= '<hr /><strong><em>DEVELOPMENT ENVIRONMENT. THIS MESSAGE WOULD NORMALLY HAVE BEEN SENT TO: ' . $to . '</em></strong>'; 
         case "staging": 
             $subject = '[STAGING] ' . $subject; 
             $to = get_option('admin_email');
             $message = 'STAGING ENVIRONMENT. THIS MESSAGE WOULD NORMALLY HAVE BEEN SENT TO: ' . $to . '\r\n' . $message; 
             $html .= '<hr /><strong><em>STAGING ENVIRONMENT. THIS MESSAGE WOULD NORMALLY HAVE BEEN SENT TO: ' . $to . '</em></strong>'; 
      $new_wp_mail = array(
		'to'          => $to,
		'subject'     => $subject,
		'message'     => $message,
		'html'        => $html,
		'headers'     => $args['headers'],
		'attachments' => $args['attachments'],
	return $new_wp_mail;

Not only will this redirect all outgoing e-mail to the e-mail address of the blog administrator (presumably you), it will also add a prefix to the subject line of the e-mail so you can easily spot development/staging messages. Finally, it adds a footer to to the body content letting you know who the message would have been delivered to if you were in the production environment.

Stepping Away From the Monitor And Onto the Slopes

Mont Ste. Marie

The last several weeks have been incredibly busy. This isn’t a complaint – I love the projects I’m working on.

Yesterday, I reminded myself how good it feels to step away from it all for a day.  There was nothing super urgent on my to-do list, the weather was decent (or at least not as bitter cold as most of this winter has been), and we’d just had a nice snowfall the day before.  So I decided to throw my gear is the car and head up to Mont Ste. Marie for a day on the slopes.

If you spend long days sitting in front of a computer screen, you should know by now that it’s important to get up, stretch, and give your eyes a break.  I find that only goes so far though.  Getting outside and doing some real physical activity isn’t only healthy for your body, but great for your brain as well.  It gives you a chance to spend an extended period of time doing something different.

I also find that while doing this, my brain is still brain is still working away on that solution to that annoying bug I’ve been chasing. Skiing also has a unique attribute for “getting away”.  If it’s cold enough, you don’t have any desire to pull your phone out of your pocket to check your messages!  I came home last night to 150+ new messages in my inbox, none of them urgent.

So today, I get to settle back into work.  Physically a little tired, but with my brain refreshed.

Add Terms to WordPress Taxonomies in Bulk

Screenshot of the BulkPress Plugin

Have you never needed to add many terms to a taxonomy (like Categories, Tags, etc.) in WordPress?  It can be rather tedious to copy and paste each term into the taxonomy editor one by one.

I had to do this a couple weeks ago, with 50+ terms, and got thinking to myself… “There’s got to be a better way!”

That’s when I discovered the BulkPress plugin by Jesper van Engelen.

This handy little plugin allows you paste a list of terms (one per line) into a text area and have each of those added to the taxonomy of your choice.  It you need to add terms as a child of another term, separate them with a forward slash.  For example:

Technology/Web Development

This would add the terms like Art and Business to the term list. Hockey would be created as a child term of Sports, and Web Development would be created as a child term of Technology.

In addition to adding the terms, it also supports customizing the slugs that are assigned to each term.  As shown in the image at the top of this post, there is an second text box that allows you to paste in slugs that lien up with each of the terms in the first text box.  If you don’t provide custom slugs, BulkPress will generate automatic ones for you.

Finally, the plugin also allows you to add bulk posts to WordPress.  Functioning the same way as terms, you can provide a list of post titles (and optionally, slugs) to be added to your site.  This feature also supports parent/child relationships.  I haven’t had a use for this feature yet, but maybe you will.


Your Pages Are Protected, But Are Your PDFs?

File Folder with a Padlock on it

Last month, I was asked to investigate why content that a website owner thought was protected behind their website’s “Members Only” area was showing up on Google, much to their horror. There are several ways of fixing this problem, and I thought I’d share one with you.

In this particular case, the website’s “Members Only” area was being protected using the WP-Members plugin, although this problem would have happened even if using WordPress’ built-in “protected post” feature.  

Child Pages

The Member’s Only page was corrected being “blocked” (WP-Members plugin terminology) – requiring a user to be logged in, but after that is where the problems started.

Although the Member’s Only page was correctly blocked, the pages deeper down into the site were not.  I can only assume here, but it looks like the person setting up the pages assumed that since you couldn’t access these pages from anywhere on the site except for the protected page, that all was good.  This is not the case.  If you knew the direct URL to the sub-pages, you could get the content as an anonymous user.  This also meant that the URL to these pages is published in sitemaps, which make it really easy for search engines, like Google, to find the content.

Lesson: Make sure all pages that should be hidden from public view are protected.


Even if your page/post is protected, any files you upload to the media library and attach to the post are still accessible if the address is known.  In this case, there were lots of PDF documents on the site, that were thought to only be accessible by “members”, since they were linked to from the pages that were in the Member’s Only area.

Blocking content from being accessed is done in WordPress through PHP code that checks which user you are, if you have the right permissions, etc.  When your web browser is loading images, PDFs, videos, and other content that isn’t a web page, WordPress does not get involved in the process.  The web server (Apache in this case) serves up the file without ever getting the PHP engine involved.  So calling will get delivered to anyone who asks for it.  Scary, right?

The solution to this problem is to have WordPress act as an intermediary between the request for the file, and the file itself, allowing permissions to be verified in the process.

Enter Download Monitor,  a handy plugin that will do exactly that, by doing two things:

  1. All files that are uploaded to the site using the Download Monitor custom content type are stored in a separate folder in WordPress (/wp-content/uploads/dlm_uploads/ to be exact).  This folder contains an .htaccess file (instructions to the Apache web server) do not allow anyone access to the files in it (“deny from all” is the rule).
  2. Links in your posts/pages point to a new path, for example: /download/my_private_file/ that is managed by the Download Monitor plugin. It checks to see if the user has the correct permissions to access the file.  If so, PHP retrieves the file on the server (because it is accessing the file directly on the hard drive, and not through a URL, the Apache .htaccess rules do not apply), and delivers the file to the browser as a download.

In addition to providing this protection of files that need to be restricted to specific users, it also gives you some additional features:

  • The ability to see how many times a file has been downloaded, right in the WordPress dashboard.
  • The ability to have different versions of the same file downloaded (think software, where you might want to make versions 1.0 and 1.1 downloadable)

Problem solved!

WordCamp Toronto: Saving Time by Managing WordPress from the Command Line (WP-CLI)

Shawn Hooper presenting WP-CLI at WordCamp Toronto 2014 (Photo Credit: Melissa Jean Clark)

Thank you to Melissa Jean Clark for snapping a few pictures during my talk.

On Sunday, I had the honour of getting to speak at WordCamp Toronto, a 2 day conference full of developers, designers, and users of the WordPress platform.  It was my first time giving this talk about the WP-CLI library, and I couldn’t have asked for a better audience.  My “talk” turned into more a group discussion.

The video of the talk will be online at some point, but in the meantime, you can see the slides here:

I also wanted to share  chunk of code that I used during the talk.  It’s my script to create a new WordPress installation from the command line, assuming you have permissions in MySQL to CREATE DATABASE.   I only recommend doing this in a development environment.

wp core download
wp core config --dbuser=root --dbname=$1 --dbpass=vagrant
wp db create
wp core install --admin_user=wcto --admin_password=wcto --title="WordCamp Toronto" --url=http://localhost/$1/

Running this script, for example:

./newsite demosite

from the command line will:

  1. Download the WordPress core
  2. Create a wp-config.php file
  3. Create a database using the connection details specified in step 2.
  4. Go the “5 minute” install of WordPress – in seconds.

Once this runs, you can login to your dashboard, you’re ready to go.

US President Barack Obama Issues Statement in Support of Net Neutrality

Today, the White House released a statement from President Obama in which he puts his support behind Net Neutrality – the concept that Internet Services providers cannot limit, throttle, or charge extra for access to some content.  All content must be treated equally.  For example, your ISP, who might also be a cable company, shouldn’t be allowed to throttle your access to Netflix.

See the full statement here:

How to Search and Replace in your WordPress Database

Database search

Search and Replace. Find and Replace.  Whatever you call it, we’ve all had to do it at some point or another.

When it comes to swapping out one string for another in WordPress, you need to be careful.  Some values are stored in the database as serialized arrays.  They look like this:


(This example is the wp_capabilities field in wp_usermeta)  If I were to do a search and replace, or an UPDATE in SQL, to change ‘administrator’ to ‘subscriber’, I would break things.  This particular array expects the string where ‘administrator’ is written to be 13 characters long.  To properly update this field, I would also have to change the s:13: before administrator to s:10: (the word subscriber is 10 characters long).  What a pain, right?

The good thing is, there’s a few solutions out there to help you do a safe search & replace in WordPress.   I’ll show you my two favourites below.

Why you’d need to do this?

The most common search and replace I do in WordPress is when moving my database from a production website to my local development environment.   The site’s domain name is scattered throughout posts, widgets, plugin settings, etc. It’s not as simple as just updating the Site URL in the WordPress settings screen. has to be replaced with localhost in a million different spots (ok, maybe I’m exaggerating a little bit).

Solution #1: Interconnect IT’s Search & Replace Tool

Screenshot of Interconnect IT's Search & Replace Tool for WordPress

Interconnect IT has made a great web-based tool for safely replacing values in WordPress’ database without breaking serialized arrays.

Once you download the search/replace tool from their website, place it (or upload it) in a new folder at the root of your WordPress site, the name of the folder does not matter.  Navigate to the folder, and the tool will run, automatically capturing the database connection details from your wp-config.php fie.

A few of the great feature about this tool are:

  1. It supports regex, so if you’re that kind of ninja, you can do some pretty powerful searching.
  2. You can do a “dry run” – where you will get to see how many changes would be made in each table.  It’s a great way of catching mistakes before doing seriously damage.

Don’t say you weren’t warned:  More than once, this utility reminds you that once you’ve completed your search & replace, make sure to delete to tool!  If someone else were to stumble upon it, they could destroy your site. (They’d also have your database credentials, since they’re right there on screen)

If you want to leave the utility installed, I’d suggest protecting it’s folder with an .htaccess file.


Solution #2: Search & Replace using WP-CLI

My preferred method of doing a search and replace is to use WP-CLI.  If you’ve never used WP-CLI, it is a set of command line tools that allow you to manage your WordPress site(s).   Not everyone will be able to use this method, you need to be hosted somewhere that supports WP-CLI.  Most shared hosts don’t, as they normally block SSH access to their servers for security reasons.  I do know that SiteGround and GoDaddy both support it now.  Of course, if you have a dedicated server or a virtual server with someone like Digital Ocean or Linode you can install it easily.

Once you have WP-CLI running, doing a search & replace is as simple as typing this:

wp search-replace old-value new-value

As with Interconnect IT’s utility, you can also do a dry-run, to make sure you’re only changing what you need to.  Simply add the –dry-run parameter to your command, like so:

wp search-replace old-value new-value --dry-runScreenshot of the results from a WP-CLI Search and Replace

Of course, with WP-CLI, you can (and should) backup your database before doing such an operation:

wp db export mydbbackup.sql
wp search-replace old-value new-value --dry-run

You can also restrict the search and replace to specific table names, ignore specific fields, or replace throughout all sites in a WordPress Multisite Network.  If you need more help with this command, just type:

wp search-replace --help

That’s it!

Both of the methods shown there are much safer than doing an UPDATE in MySQL, or a Find & Replace in a .SQL dump of your database.   Good luck, and happy replacing!

Twitter Analytics Now Available to Everyone

Twitter Analytics Graph

As a fan of Twitter, and of data, today is a good day.  Twitter’s analytics tool, which had previously only shown some data to regular (read: not advertisers) users of it’s service, has opened it’s full set of reports up.

In addition to statistics about your followers, which has been available for some time, you now get access to analytics for every tweet you’ve sent, including:

  • The number of times your tweet as appeared in someone’s Twitter feed.
  • The number of engagements (opens, favorites, replies, hashtag tickets, retweets, etc.) that the tweet has received
  • The engagement rate (as a percentage) of the tweet

Screenshot from Twitter Analytics showing impression and engagement rates on every tweet.

You also get a summary of the engagement that all of your tweets has had over the past 28 days, compared to the previous 28-day period.

Twitter Analytics 28-Day Summary

If you want to get started looking at your stats, visit:

The Happiness Bar

Volunteers sitting at a table with laptops assisting WordCamp attendees

I’ve just returned from WordCamp Montreal 2014, a two day gathering of WordPress users, designers, developers and practitioners in fields related to the incredibly popular publishing platform. (Did you know that WordPress now powers 22% of all websites?)

Aside from the informative sessions, the abundance of networking sessions, and some great meals too one of the highlights of a WordCamp for me is something called The Happiness Bar.

The Happiness Bar doesn’t serve up your favourite alcoholic beverage, but it does quench your thirst for knowledge. Staffed by volunteers (actually, everything at WordCamp is volunteer), the Happiness Bar is a place to drop in an ask any questions you might have about WordPress.

There are a couple of great reasons to volunteer at the Happiness Bar:

  1. You get to help. I volunteered for two one-hour shifts at the bar on the weekend, and was able to help a food blogger migrate her website from to a self-hosted solution, giving her the flexibility to do more with her site. I was also able to teach an aspiring developer a few tricks to writing cleaner code.
  2. You get to see some of the great projects that people in the community are working on.
  3. You end up learning some great tips and tricks from your fellow Happiness Bar volunteers.

If you’re attending your local WordCamp, I suggest you take the time to visit the Happiness Bar – either to ask a question, or to lend a hand.