Sending Complex HTML Email

Once you start sending HTML email, odds are good that you’ll want to start sending pretty HTML email, complete with graphics. You can include HTML links to resources on your site, but many mail programs block those, because they’re potential violations of privacy. Instead, it often makes more sense to send the graphics as part of the email message, as a multipart attachment.

Warning

There are fewer and fewer people who deeply object to HTML email, but there are still a lot of people who get annoyed by huge messages that take a long time to download. If you wouldn’t feel comfortable sending a message to a relative on dial-up, you probably shouldn’t send it to anyone, unless they’ve specifically requested something they know is large.

ActionMailer can handle most of the logistics for this, using the same tools shown earlier, but it needs one additional library to make it work. From the command line, or the Heroku Gems & Plugins Manager, add the InlineAttachment gem:

gem install InlineAttachment

One of the nicer features of multipart email is that it can include a plain-text version as well as HTML and graphics, so app/views/awards/show.html.erb can revert to the checkbox-free version shown earlier in Example 17-5. Similarly, because the awards_controller no longer needs to evaluate the checkbox, app/controllers/awards_controller.rb can revert to the version shown in Example 17-2. (The code for this example is in ch17/students012.)

Most of the work needed to send the message now falls into the AwardMailer model, in app/models/award_mailer.rb, and two views. The new AwardMailer has a much more complicated certificate method, shown with changes highlighted in Example 17-10.

Example 17-10. A model for sending multipart email messages

class AwardMailer < ActionMailer::Base

 def certificate(award, email)

  subject        award.name
  recipients     email
  from          'School System <[email protected]>'
  sent_on        Time.now
  content_type  'multipart/alternative'

  # explicitly enumerate our alternative parts, as we want control
  # for the HTML part

  part :content_type => 'text/plain',
   :body => render_message('certificate.text.plain.erb', :award => award)

  # unique content identifier for the image we'll use - adjust domain for you
  @cid = Time.now.to_f.to_s + "[email protected]"

  part 'multipart/related' do |p|
   p.parts << ActionMailer::Part.new(:content_type => 'text/html',
    :body => render_message('certificate.text.html.erb',
     :award => award, :cid => @cid))

   p.inline_attachment :content_type => "image/png",
    :body => File.read(File.join(RAILS_ROOT, 'public', 'images', 'rails.png')),
    :filename => "rails.png",
    :cid => "<#{@cid}>"
  end
 end
end

This is a very active model, with much more going on than its text or HTML predecessors. The first change, which makes the rest possible, is the shift in content_type from text/plain or text/html to the much more flexible multipart/alternative. When an email client gets a message with a MIME type of multipart/alternative, it will look through the message first before simply displaying it, choosing which part to show the user based on the user’s preferences.

Note

If you just want to send HTML email with multiple pieces, multipart/related is probably a better choice, but the approach outlined here is definitely more appealing if any of the recipients will be reading the message in a plain text viewer. You can also use multipart/mixed to send attachments.

The next few sections define the parts of multipart. The first part call defines the plain text message to send, as the text message normally is, at the start of the message:

part :content_type => 'text/plain',
   :body => render_message('certificate.text.plain.erb', :award => award)

It specifies a MIME content type of text/plain. The :body parameter calls render_message, explicitly specifying a view and passing it the same :award => award parameter that the older version of certificate had used in Example 17-3. The view, in app/views/award_mailer/certificate.text.html.erb, contains exactly the same contents as Example 17-4.

The next part call is much more complicated:

@cid = Time.now.to_f.to_s + "[email protected]"

  part 'multipart/related' do |p|
   p.parts << ActionMailer::Part.new(:content_type => 'text/html',
    :body => render_message('certificate.text.html.erb',
     :award => award, :cid => @cid))

   p.inline_attachment :content_type => "image/png",
    :body => File.read(File.join(RAILS_ROOT, 'public', 'images', 'rails.png')),
    :filename => "rails.png",
    :cid => "<#{@cid}>"
  end

The @cid variable provides a Content-ID header for the attached graphic. Content-IDs are supposed to be unique. The Time object, here called with now to get the current time, to_f to convert that to a number of seconds, and to_s to turn the number into a string, provides a reasonably unique identifier when placed at the front of the string. The next piece is more like a filename. It doesn’t have to be the same as the name of the file that’s sent, but it’s simpler that way. The @ sign separates what is effectively the filename from its source, so you should replace example.com with the name of your own domain.

Once the @cid variable is set, the part call begins, defining a multipart/related part of the message. This represents the actual HTML with graphics combination. It takes a block, using do |p| to set context for the calls which follow. The two calls inside of the block both reference p to add their content.

The first references the p.parts array, part of the TMail library Rails uses to create and process email, and appends (<<) to it a new ActionMailer::Part. The parameters define the contents of the part, setting the content_type to text/html and calling the render_message method again to use the certificate.text.html.erb view, shown in Example 17-10, passing it both the award value and the @cid value, which it needs to connect to the graphic.

The second call is to the inline_attachment method, which was the key piece the InlineAttachment gem provided. It also has a content_type parameter, set to image/png in this case to represent the graphic. The :body parameter identifies which graphic. The File.read method takes in a file, at a location identified by the File.join method and its arguments. Why not just write something like RAILS_ROOTpublicimages ails.png? Because there are too many different ways to specify file locations on different operating systems, and File.join helps work around that problem. The :filename argument specifies the filename that should be provided for the file—it doesn’t have to be the same as the original file. The :cid argument provides the Content-ID for the graphic as well. The HTML, generated by the view shown in Example 17-11, will reference the graphic by Content-ID, not by filename.

Example 17-11. The certificate.text.html.erb view for generating HTML inside a multipart email message, referencing a graphic through its Content-ID

<table width="100%" border="1" cellpadding="10">
  <tr>
    <td align="center">
      <p>
        School of Rails<br />
        <img src="cid:<%= @cid %>" />
      </p>
      <br />
      <br />
      <h1><%=h @award.name %></h1>
      <br />
      <br />
      <p>awarded to</p>
      <br />
      <br />
      <h2><%=h @award.student.name %></h2>
      <br />
      <h3><%=h @award.year %></h3
    </td>
  </tr>
</table>
<br />
<br />
<hr />

<p>Courtesy of the School System!</p>

Now, if a user clicks on the “Email me this award” button on an award page, they’ll get a message, complete with a graphic, like that shown in Figure 17-7.

A mail message containing a graphic, sent by the fictional School of Rails, whose logo is familiar

Figure 17-7. A mail message containing a graphic, sent by the fictional School of Rails, whose logo is familiar

The Rails graphic happened to be around, but you can use any images you’d like. Each will need its own Content-ID, and you’ll need to specify the path for reaching them. Figure 17-8 illustrates the process of assembling, packaging, and sending this message.

Steps in assembling and sending a multipart message

Figure 17-8. Steps in assembling and sending a multipart message

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset