In the first post in this series, we took a look at the variety of emails that WordPress sends depending on how it's used. We also discussed how it's possible to customize emails that WordPress sends without actually having to outsource the functionality to third party services.
We also reviewed the various filters that WordPress provides for hooking into the system in order to customize our emails; however, because the number of emails that WordPress sends is so large, we've opted to take a focused look at comment moderation and comment notification emails.
So in this article, we're going to be building a plugin that will allow us to completely customize the look and feel of comment-specific emails. Ultimately, this should give you some insight as to how the available hooks work, what's required to customize the emails, and how you can take advantage of the WordPress API to customize the content of your emails.
Preparing The Plugin
Because the purpose of this article is meant to serve as a tutorial for building a plugin, let's go ahead and begin with the practical steps necessary to get us started.
Though this entire project is available on GitHub for reference, I highly recommend following along with the steps in this article before downloading the working plugin. After all, it's meant to provide a reference for when things go wrong - not to help you jump to the conclusion.
Setup The Directory
The first thing that we need to do is setup a directory for our plugins. We can do this by creating a custom-comment-emails
directory in wp-content/plugins
.
Next, create the following directories and files:
lang/
lang/plugin.po
plugin.php
Note that the lang
directory will be used to keep our localization files for others to provide translations.
The Localization File
In plugin.po
add the following text:
msgid "" msgstr "" "Project-Id-Version: Custom Comment Emails\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-12-05 14:17-0500\n" "PO-Revision-Date: 2012-12-05 14:17-0500\n" "Last-Translator: Tom McFarlin \n" "Language-Team: Tom McFarlin \n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-KeywordsList: __;_e\n" "X-Poedit-Basepath: .\n" "X-Poedit-SourceCharset: utf-8\n" "X-Generator: Poedit 1.5.4\n" "X-Poedit-SearchPath-0: ..\n"
Obviously, you'll want to customize this to meet your needs, but you get the general idea. Once done, make sure that you grab a copy of POEdit as we'll be using this program to generate the MO files which are used for localization.
The Plugin File
We'll be taking an object-oriented approach to building our plugin so let's go ahead and stub out the class that will serve as the core of our plugin.
For this plugin, we need to provide the header definition, a constructor, three functions, and a helper method. Easy enough, right?
<?php Plugin Name: Custom Comment Emails Plugin URI: http://github.com/tommcfarlin/custom-comment-emails/ Description: A plugin used to demonstrate how to create custom comment email notifications. The corresponding series of articles will run on Envato's TutsPlus Network. Version: 1.0 Author: Tom McFarlin Author URI: http://tommcfarlin.com License: Copyright 2012 Tom McFarlin ([email protected]) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ class Custom_Comment_Email { /*--------------------------------------------* * Constructor *--------------------------------------------*/ /** * Initializes the plugin by setting localization, filters, and administration functions. */ function __construct() { // TODO } // end constructor /*--------------------------------------------* * Localization *--------------------------------------------*/ /** * Loads the plugin text domain for translation */ function plugin_textdomain() { // TODO } // end plugin_textdomain /*--------------------------------------------* * Filters *--------------------------------------------*/ /** * Sets the headers for the email being sent for the comment notification email. * * @since 1.0 */ function email_headers() { // TODO } // end email_headers /** * Creates the customized subject for the comment notification email. * * @param string $subject The content of the subject * @param int $comment_id The ID of the comment for which this subject is being sent * @return The subject line for the email * @since 1.0 */ function email_subject( $subject, $comment_id ) { // TODO } // end email_subject /** * Creates a customized, styled email used to notify users that they have a new comment. * * @param string $message The content of the email * @param int $comment_id The ID of the comment being left * @return The customized body content of the email * @since 1.0 */ function email_text( $message, $comment_id ) { // TODO } // end email_text } // end class new Custom_Comment_Email();
We'll work on filling out these functions throughout this article, but go ahead and review the code comments so that you have a clear understanding of where we're headed.
Setting Up The Constructor
The first thing that we need to do is to prepare the constructor. Recall that this part of the class is responsible for initializing the plugin, setting up localization information, and specifying any hooks.
Since we've already stubbed out the necessary functions, it should be easy to follow with what's going on, but code comments have been provided for completeness.
function __construct() { // Load plugin text domain add_action( 'init', array( $this, 'plugin_textdomain' ) ); /* Set the filters for comment approval and the comment notification email. * For purposes of this example plugin, these will be the same email. * Though in a production environment, you'd naturally want to include the typical * 'Approve,' 'Spam,' and 'Trash' links. */ // Moderation add_filter( 'comment_moderation_headers', array( $this, 'email_headers' ) ); add_filter( 'comment_moderation_subject', array( $this, 'email_subject' ), 10, 2 ); add_filter( 'comment_moderation_text', array( $this, 'email_text' ), 10, 2 ); // Notifications add_filter( 'comment_notification_headers', array( $this, 'email_headers' ) ); add_filter( 'comment_notification_subject', array( $this, 'email_subject' ), 10, 2 ); add_filter( 'comment_notification_text', array( $this, 'email_text' ), 10, 2 ); } // end constructor
In short, we've specified the function to set the plugin's textdomain for localization purposes and we've specified the hooks that we'll be using. Namely, those for comment moderation and comment notification.
Localization
Next, let's define the function that will retrieve the plugin's localization files from the lang
directory that we created when initializing this plugin.
This is a simple, single line call that's provided by the WordPress API.
/** * Loads the plugin text domain for translation */ public function plugin_textdomain() { load_plugin_textdomain( 'custom-comment-email-locale', false, dirname( plugin_basename( __FILE__ ) ) . '/lang' ); } // end plugin_textdomain
The key takeaway from this particular function is the key that we've used to identify this plugin's locale. For those who are unfamiliar, this must be unique as it's what will allow translators to provide localized versions of strings that we'll be adding throughout the remainder of this plugin.
Other than that, localization is good to go.
Filters
Before we actually implement the functionality for our notification emails, there's an import distinction that we need to make. Specifically, we need to talk about the difference in moderation emails and in notification emails.
By default, WordPress requires that any comment left associated by an email address that isn't recognized by authorized. This triggers an email to be sent to the site administrator (or the blog post author) to approve the comment. From that point forward, the comments will be automatically approved.
That said, WordPress does allow administrators to disable this moderation functionality and let anyone leave a comment regardless of if they've previously done so.
I bring this up because this influences the way that we'll be developing this plugin. There are six hooks - three for moderation and three for notification - all of which are similar in that they are related to an email's header, subject line, and message.
The hooks are as follows:
- comment_moderation_headers
- comment_moderation_subject
- comment_moderation_text
- comment_notification_headers
- comment_notification_subject
- comment_notification_text
To make things simple, we'll be associating our three functions with all six plugins. This means users will see the same email for moderation as well as for notification.
In a production environment, this isn't necessarily ideal; however, to demonstrate how to take advantage of these hooks and how to customize emails, it works well.
The Subject
First, let's begin with the simple task of updating the email's subject line. This doesn't require that we work with any markup (which is somewhat of a pain in email clients anyway, as you'll soon see :), and it's a simple function that we can easily customize.
For our example, let's rewrite the email's subject line to read:
[Post Title] Hey There - Looks like you've got a new comment!
To do this, locate the email_subject
function and then update it to include the following. Code comments are provided for completeness, but we'll discuss it a bit more just after the function:
/** * Creates the customized subject for the comment notification email. * * @param string $subject The content of the subject * @param int $comment_id The ID of the comment for which this subject is being sent * @return The subject line for the email * @since 1.0 */ function email_subject( $subject, $comment_id ) { // Create the subject line in the following format: "[Post Title] Hey There - Looks like you've got a new comment!" $subject = __( "[", 'custom-comment-email-locale' ) . $this->get_post_title( $comment_id ) . __( "]", 'custom-comment-email-locale' ); $subject .= " "; $subject .= __( "Hey There - Looks like you've got a new comment!", 'custom-comment-email-locale' ); return $subject; } // end filter_method_name
Notice that the function accepts two parameters - subject, and comment ID. The subject is the original subjet that's being passed into the function. This is useful if you want to prepend or append text to the subject line, but we're going to be writing our own so it will actually be overwritten.
The comment_ID is useful because it will allow us to retrieve a variety of information such as the post, the post's title, and so on simply from using the comment ID.
In fact, that's how we retrieve the post title for the subject line. Notice, however, that in our post we're making a call to $this->get_post_title( $comment_id )
.
A Small Helper Function
This is the helper function that we've defined in order to help us easily retrieve the post title associated with the given comment. We've abstracted it into a helper function so that we can use it later in the plugin.
The helper function is simple:
/** * Retrieves the ID of the post associated with this comment. * * @param int $comment_id The ID of the comment that we're using to get the post title * @return string The title of the comment's post * @since 1.0 */ private function get_post_title( $comment_id ) { $comment = get_comment( $comment_id ); return get_the_title( $comment->comment_post_ID ); } // end get_post_title
Clear, right? Get a reference to the comment object by the specified ID, then return the title of the post that's associated with the given comment's post ID.
At this point, feel free to test your code. Assuming that you followed the steps in the first article, you should have a development environment setup that's sending emails to your inbox.
Activate the plugin, leave a comment on the post and you should see a new subject line.
Neat, huh?
The Email Text
Next, we're ready to begin actually setting the email text. In our plugin, let's have the email include the folllowing:
- A header that reads "Comment For [Post Title]"
- An area for the content that reads "The original contents of this email read:" after which we'll include the original comments
- A simple footer that denotes if this is a normal comment or a trackback or pingback, and that includes the comment author's email address.
We'll also provide some styling to demonstrate how we can easily customize the look and feel of an email.
So locate the email_text
function and include the following:
/** * Creates a customized, styled email used to notify users that they have a new comment. * * @param string $message The content of the email * @param int $comment_id The ID of the comment being left * @return The customized body content of the email * @since 1.0 */ function email_text( $message, $comment_id ) { // Retrieve the comment $comment = get_comment( $comment_id ); // Define the header $message = '<h1 style="font-size 1.5em; display: block;">'; $message .= __( 'Comment For ', 'custom-comment-email-locale' ); $message .= $this->get_post_title( $comment_id ); $message .= '</h1>'; $message .= '<div style="width: 100%; border-top: 1px solid #ededed; margin-bottom: 12px;">'; $message .= ' <h3>' . __( 'The original contents of this email read:', 'custom-comment-email-locale' ) . '</h3>'; $message .= $comment->comment_content; $message .= '</div> '; // Determine what type of comment this is: $comment_type = __( 'normal comment.', 'custom-comment-email-locale' ); if( '' != $comment->comment_type ) { $comment_type = __( 'trackback or a pingback.', 'custom-comment-email-locale' ); } // end if // And set the footer $message .= '<div style="background: #ededed; border-top: 1px solid #ededed; padding: 4px; text-align: center;">'; $message .= __( 'This comment was left by ', 'custom-comment-email-locale' ) . '<strong><a href="mailto:">comment_author_email . '">' . $comment->comment_author_email . '</a></strong>.'; $message .= __( ' | ', 'custom-comment-email-locale' ); $message .= __( 'This is a ', 'custom-comment-email-locale' ) . '<strong>' . $comment_type . '</strong>.'; $message .= '</div>'; return $message; } // end email_text
Though the code comments should be relatively clear, note that we're doing the following:
- Retrieving the comment
- Setting up a header for the email (where we also retrieve the post title using our helper function)
- Writing out the original code comment
- Defining a footer that shows the type of comment and the comment author
Note also that we've written some inline styles. I'm assuming that you'll be using Gmail as your mail client when testing these emails; however, every email client handles styles differently so be sure to review this chart if you end up doing this in some of your production-ready projects.
Once done, trigger another email and you should see something like the following:
Oops! That's not what we want. Luckily, this is an easy fix and it requires one more line of code in the plugin.
Setting The Headers
In order to send styled, HTML-based messages using WordPress, we need to set the headers of the email properly. To do this, update the email_headers
function to look like this:
/** * Sets the headers for the email being sent for the comment notification email. * * @since 1.0 */ function email_headers() { add_filter( 'wp_mail_content_type', create_function( '', 'return "text/html";' ) ); } // end email_headers
This informs the email client to render the content as HTML. Permitting that you've done this right, you should see something like the following:
Much better!
Conclusion
Obviously, we've only scratched the surface of just how powerful customizing your WordPress-based emails can be especially if you take the time to generate some nice markup, elaborate styles, and so on.
In my opinion, the most important thing to understand is how to hook into the filters that are provided that allow us to do all of the above.
Hopefully, this series has provided enough information to get your started. Remember to check out the plugin on GitHub, and good luck with those custom emails!
Comments