Setting up Multipart Email Templates for WordPress

This article is part of a three part series exploring how to configure the WordPress content management system to send multipart emails. In part 2, I go through how you can add email templates to your theme.

As mentioned in part 1 of this series, many plugins extend the functionality of WordPress enabling the system to send HTML emails. There’s also a great tutorial on the Smashing Magazine website that show’s you how to configure the default emails produced by WordPress to send in HTML format.

Here I’m going to take these ideas a step further and look at how we can use similar techniques to send multipart emails. In the next article in the series I’ll be going through the setup of a PHP class to construct and control the sending of our multipart emails from WordPress. Before we can do that however, we’ll need to setup some email templates that can be used for the purpose.

We’re going to be storing these templates in our active theme folder just as we would with regular page templates. If you’re a theme developer with complete control over the theme you are using this approach should be fine. If you’re using a theme developed by others, you may want to consider loading your templates from a plugin folder instead so that they aren’t overwritten by theme updates.

The reason I’m choosing to use the theme folder is because I think this maintains the ethos of using the theme for styling and plugins for functionality. By storing our email templates in the theme, we can develop new email templates that can be packaged with themes keeping the plugin responsible for functionality only.

Tip: Coding for email is a large subject which I won’t be covering in much depth in this article. Due to the range and variety of email clients many ‘hacks’ and ‘fixes’ can be utilised to achieve the things we take for granted when developing for web browsers. Check out this Mailchimp article for a brief guide on coding for email basics.

Constructing our Multipart Email Templates

Let’s get started! In order to code an email that works as we expect across a wide range of email clients, we need to think like it’s 1999. Table based layouts and inline styles are your friend here as the technology which underpins most email clients is way behind that used in most web browsers. Outlook 2016 for example uses the Microsoft Word HTML rendering engine rather than the engine used by the Microsoft web browsers. The results of this are best described as “quirky” to say the least.We’ll be breaking our templates down into three files(header.php, footer.php and body.php) for each content type, plain text and html.

Here’s an example of the folder structure to setup in your theme folder for your email templates:

your_theme/template-parts/emails/plain/
your_theme/template-parts/emails/html/

You can use a different folder structure within your own theme if you prefer.

Plain Text Templates

Let’s build our plain text header.php file and save it in the /plain/ folder we just created:

<?php
/**
 * Plain text email header.
 *
 * The email template part used for the plain text email header.
 *
 * @param  $subject The subject of the email being sent.
 * @package WordPress
 * @subpackage My Multipart Email
 * @since 1.0.0 
 * @version 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Construct email header
$message = "=====================================================================\n";
$message .= get_bloginfo( 'name' ) . "\n";
$message .= get_bloginfo( 'description' ) . "\n";
$message .= wordwrap( $subject, 100, "\n" ) . "\n";
$message .= "=====================================================================\n\n";

We’re checking that the 'ABSPATH' constant is defined at the top of our file so that the template file cannot be accessed from outside of the WordPress environment. We’ll perform the same check in each of our template files for security.

Our header file starts off a $message variable through which we construct our plain text email line-by-line.

We’ll pass a $subject variable to the header.php template to be included in our email header. Using the PHP wordwrap() function we’ll make sure that any subject line which is passed to the template is wrapped every 100 characters using the newline character (“\n”).The get_bloginfo() WordPress function will bring in data from the database to construct the rest of our email header.

Now let’s build the plain text body.php file and save it in the same /plain/ folder:

<?php
/**
 * Plain text email body.
 *
 * The email template part used for the plain text email body.
 *
 * @param  $message_body The message content for the email being sent.
 * @package WordPress
 * @subpackage My Multipart Email
 * @since 1.0.0 
 * @version 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Get template file for email header from theme
require( locate_template( 'template-parts/emails/plain/header.php' ) );

$message .= wordwrap( $message_body, 100, "\n" ) . "\n";

// Get template file for email footer from theme
require( locate_template( 'template-parts/emails/plain/footer.php' ) );

In the body.php file we use the require() core php function combined with WordPress’ locate_temaplate() function to include our email header and footer files. By using require() and locate_template() we can pass variables to our template files so they can be used to construct the message content. If we were to use the get_template_part() WordPress function instead, we would not be able to use variables in this way.

We pass a $message_body variable containing our message to the template file for output. once again, using wordwrap() we’ll wrap the message every 100 characters to keep things nice and structured.

Now let’s build the plain text footer.php file and save it in the same /plain/ folder:

<?php
/**
 * Plain text email footer.
 *
 * The email template part used for the plain text email footer.
 *
 * @package WordPress
 * @subpackage My Multipart Email
 * @since 1.0.0 
 * @version 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Construct email footer
$message .= "\n=====================================================================\n\n";
$message .= sprintf( __( 'This is an automated message sent from %s', 'my_multipart_email' ), esc_url_raw( get_bloginfo( 'url' ) ) ) . "\n\n";
$message .= wordwrap( __( "This message is intended for the addressee only and may contain private and confidential information or material which may be privileged. If this message has been sent to you in error you must delete it immediately and should not copy it or show it to any other person.", 'my_multipart_email' ) . "\n\n", 100, "\n", false );
$message .= "=====================================================================";

echo $message;

Using the sprintf() and __() translation function, we can construct a translatable footer for our email containing a simple catch-all disclaimer. Once our footer text has been constructed, we’ll echo the complete $message to print our completed plain text message body.

HTML Templates

Now let’s start building our HTML templates beginning with the header.php file. We’ll save it in the /html/ folder we created earlier:

<?php
/**
 * HTML email header.
 *
 * The email template part used for the HTML email header.
 *
 * @param  $subject The subject of the email being sent.
 * @package WordPress
 * @subpackage My Multipart Email
 * @since 1.0.0 
 * @version 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Construct email header
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?php bloginfo( 'charset' ); ?>" />
<!-- overriding mobile auto styling meta -->
<meta name="format-detection" content="telephone=no" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<title><?php echo esc_html( $subject ); ?></title>
</head>

<body topmargin="0" leftmargin="0" marginheight="0" marginwidth="0" style="margin: 0; padding: 0; width:100% !important; -webkit-text-size-adjust: none !important; -ms-text-size-adjust: none !important; -moz-text-size-adjust: none !important; -webkit-font-smoothing: antialiased;" bgcolor="#fafafc">
  
  <!-- start of body imposter -->
  <table cellpadding = "0" cellspacing = "0" width = "100%" border = "0" bgcolor = "#fafafc">
    <tr>
      <td align = "left" valign = "top" style = "padding-top: 15px; padding-right: 15px; padding-bottom: 15px; padding-left: 15px; border-bottom: 1px solid #dbdde0;">
      
        <!-- start of container table -->
        <table align = "center" cellpadding = "0" cellspacing = "0" width = "600" border = "0">
          <tr>
            <td align = "left" valign = "top">
              
              <!-- start of site info and subject table -->
              <table cellpadding = "0" cellspacing = "0" width = "100%" border = "0">
                <tr>
                  <td align = "left" valign = "top">
                    <span style = "font-family: Georgia, 'Times New Roman', Times, serif; font-size: 30px; color: #3f4349; line-height: 1.5;">
                        <a href = "<?php echo esc_url( get_bloginfo( 'url' ) ); ?>" target = "_blank" style = "text-decoration: none; color: #a85516;">
                          <?php echo esc_html( get_bloginfo( 'name' ) ); ?>
                        </a>
                        <br />
                    </span>
                    <span style = "font-family: Georgia, 'Times New Roman', Times, serif; font-size: 20px; color: #3f4349; line-height: 1.5;">
                        <?php echo esc_html( get_bloginfo( 'description' ) ); ?>
                        <br />
                        <?php echo esc_html( $subject ); ?>
                    </span>
                  </td>
                </tr>
              </table>
              <!-- end of site info and subject table -->
            
            </td>
          </tr>
        </table>
        <!-- end of container table -->
        
      </td>
    </tr>
  </table>
  <!-- end of body imposter -->

The HTML structure shown here is a table based structure I use for email which I have found to provide good results in a wide variety of email clients. Feel free to experiment with the HTML structure in your own templates to find what works for you.

Once again we’ll be passing the $subject variable to the template. As we’re now using this variable in HTML output, we’ll escape the output using the esc_html() WordPress function. We’ll also escape data brought in with the get_bloginfo() WordPress function depending on how we’re using it in our HTML template. We use esc_url() for example when constructing a link back to the website in the template.

Now for the HTML body.php file:

<?php
/**
 * HTML Email Body
 *
 * The email template used for the HTML message body.
 *
 * @param $message_body The text string to send in the message body.
 * @package WordPress
 * @subpackage My Multipart Email
 * @since 1.0.0 
 * @version 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Get template file for email header from theme
require( locate_template( 'template-parts/emails/html/header.php' ) );
?>  
  <!-- start of body imposter -->
  <table cellpadding = "0" cellspacing = "0" width = "100%" border = "0" bgcolor = "#fafafc">
    <tr>
      <td align = "left" valign = "top" style = "padding-top: 15px; padding-right: 15px; padding-bottom: 15px; padding-left: 15px; border-bottom: 1px solid #dbdde0;">
      
        <!-- start of container table -->
        <table align = "center" cellpadding = "0" cellspacing = "0" width = "600" border = "0">
          <tr>
            <td align = "left" valign = "top">
              
              <!-- start of main content table -->
              <table cellpadding = "0" cellspacing = "0" width = "100%" border = "0">
                <tr>
                  <td align = "left" valign = "top" style = "padding-top: 15px; padding-bottom: 15px;">
                  
                    <!-- start of text container table -->
                    <table cellpadding = "0" cellspacing = "0" width = "100%" border = "0">
                      <td align = "left" valign = "top" style = "padding-bottom: 15px; border-bottom: 1px solid #dbdde0;">
                        <span style = "font-family: Verdana, Arial, sans-serif; font-size: 14px; color: #3f4349; line-height: 1.5;">
						  <?php echo nl2br( esc_html( $message_body ) ); ?>
                        </span>
                      </td>
                    </table>
                    <!-- end of text container table -->
                    
                  </td>
                </tr>
                
                <?php 
		// Get template file for email footer from theme
		require( locate_template( 'template-parts/emails/html/footer.php' ) );

We use the same require() technique here as we used in our plain text templates to bring in our other template files. I’ve escaped the $message_body variable for use in a HTML environment. I’ve also added the core nl2br() PHP function so that if $message_body contains newline breaks for formatting these will be converted into the HTML <br /> tag for use in the template environment.

Our final email template is the HTML footer.php file. Let’s go ahead and construct this now:

<?php
/**
 * HTML email footer.
 *
 * The email template used for the HTML email footer.
 *
 * @package WordPress
 * @subpackage My Multipart Email
 * @since 1.0.0 
 * @version 1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Construct email footer
?>
                <!-- start of footer -->
                <tr>
                  <td align = "left" valign = "top" style = "padding-bottom: 15px;">
                  
                    <!-- start of text container table -->
                    <table cellpadding = "0" cellspacing = "0" width = "100%" border = "0">
                      <td align = "left" valign = "top">
                        <span style = "font-family: Verdana, Arial, sans-serif; font-size: 14px; color: #3f4349; line-height: 1.5;">
                          <?php
						    printf( __( 'This is an automated message sent from %1$s%2$s%3$s', 'my_multipart_email' ), 
								'<a href = "' . esc_url( get_bloginfo( 'url' ) ) . '" target = "_blank" style = "font-style: italic; color: #a85516;">', 
								esc_html( get_bloginfo( 'name' ) ),
								'</a>'  
							);
						  ?>
                          <br />
                          <br />
                        </span>
                        <span style = "font-family: Verdana, Arial, sans-serif; font-size: 14px; color: #3f4349; line-height: 1.5;">
                          <?php
                          	_e( "This message is intended for the addressee only and may contain private and confidential information or material which may be privileged. If this message has been sent to you in error you must delete it immediately and should not copy it or show it to any other person.", 'my_multipart_email' );
						  ?>
                        </span>
                      </td>
                    </table>
                    <!-- end of text container table -->
                    
                  </td>
                </tr>
              </table>
              <!-- end of main content table -->
            
            </td>
          </tr>
        </table>
        <!-- end of container table -->
        
      </td>
    </tr>
  </table>
  <!-- end of body imposter -->

</body>
</html>

In our HTML footer file I’ve used the printf() PHP function to create a link to the website we’re sending from within a translatable string. A second translatable string contains the same generic disclaimer message we used in our plain text template.

The footer.php file closes any open table markup and closes our open <body> and <html> tags, finishing off our HTML message body.

Outcome

We now have all the template files we need to send out a multipart email from the WordPress system. Read the next article in the series to see how to combine these templates with a plugin to control the sending of multipart emails. 

Please note: while this series explores a way in which we can configure WordPress to send multipart emails, the plugin it produces is meant for testing purposes only and not for use in a live environment.

Tags: html, php