Rails actionmailer escaping HTML with link_to - ruby-on-rails

I am using Action Mailer and trying to include a link. For some reason the HTML is being escaped and, rather than showing a link, it is showing:
here
Instead it should just show the link. Here is the Ruby:
<%= raw(link_to("here", employer_url(:task_review => 'true'))) %>
I have also tried:
<%= link_to("here", employer_url(:task_review => 'true')).html_safe %>
and
<%= link_to("here", employer_url(:task_review => 'true')) %>
None of them seem to work. I've looked at other SO answers to similar questions and they all say to use either raw or html_safe, but neither work. Does anyone have any ideas?

From the description of your problem it looks like the problem is not in the template rendering, but in the content type.
An email client accepts different content types, and renders accordingly - parses and renders the HTML if the content type is text/html, or shows the text as-is if the content type is text/plain.
Many mailers send mail containing more than one version of the mail (one HTML and one TEXT), to support old mail clients which don't have HTML capabilities.
To check whether the mail your program sent has an HTML version, open its raw message (in Gmail that would be under More -> show original) and look for text looking like this:
------=_NextPart_001_0048_01CE7CA4.8389CCD0
Content-Type: text/html;
charset="windows-1255"
This means that there is an HTML version of the mail you sent.
If however, all you see is something like:
------=_NextPart_001_0048_01CE7CA4.8389CCD0
Content-Type: text/plain;
charset="windows-1255"
your problem is there. (if both options appear - your mail client may not support HTML)
If you find that you do not send an HTML version, make sure your template file has the extension .html.erb rather .text.erb (the latter indicates to the mailer that you want a text-only mail format).

in config/environments/development.rb
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
This may solve the problem.

For ActionMailer you should build the URL with the url_for helper because other helpers like link_to are not available in ActionMailer, or include the helper methods in your ActionMailer class:
<%= url_for controller: 'login',
action: 'verify',
only_path: false,
host: 'http://myawesomesite.com',
params: { token: '12hew' } %>

Related

Why do image tags work in regular views but not mailer views?

Why does the below code show images on regular Rails views (webpages) but not show the images when used in mailer views (i.e. the emails I send from the app)? The images are located in /app/assets/images.
<%= image_tag "hr1.png" %>
<%= image_tag "pic_mountain.jpg" %>
<%= link_to image_tag("hr1.png", alt: "Sample alt text"), 'http://google.com' %>
As a follow up, is this the best way to include images in html emails being sent to users? Should I somehow store the images at www.url.com/images/sample_pic.jpg? If so, how do you do this? I'm using mailer classes I created that inherit from ActionMailer. Also, this is Rails 4.1.6. Thanks!
The ERB you posted generates html of the form
<img src="/assets/hr1.png" />
(Iglorying asset digests for brevity), ie it contains only the path to the image and the browser uses the host and protocol the containing page was loaded from.
In an email this doesn't work because there is no such host. You can either include the host in the url or add the images as inline attachments.
The most basic way for the first is to do something along the lines of
image_tag(image_url("foo", host: "example.com"))
You can also set default_url_options on your mailer so that you don't have to do this over and over again.
For inline attachments you first add the image as an attachment (in your mailer)
attachments.inline['foo.png'] = File.read(Rails.root.join("app", "assets", "images", "foo.png")
And then in your view you use it like so:
<%= image_tag attachments['foo.png'].url -%>
Note that this turns your email into a multipart email, so you shouldn't try and force the content type to text/html

ActionMailer can I have an html and plain text partial with same name but different extensions?

For each ActionMailer email view I have an html and text file, like so:
new_user_welcome.html.haml
new_user_welcome.txt.haml
Using this convention, ActionMailer automatically handles sending html and plain text emails for me.
Can I do this with email partials too, to get a html and txt version of the same partial? For example, can I name my email footer partials like this, and will ActionMailer automatically use the right one used in the html and text emails?
_email_footer.html.haml
_email_footer.txt.haml
You can have partials for your mails too:
_email_header.html.erb
_email_footer.html.erb
Then you can refer to them inside each of your emails:
<%= render :partial => 'email_header' %>
... Email body here...
<%= render :partial => 'email_footer' %>
Repeat the process for txt emails partials.

Rails and HAML: Mailto link with picture

I am trying to create a mailto link that involves clicking a picture and including some raw content from a text file into the body of the message.
The following doesn't work (haml).
= mail_to "friend#example.com" do
%img{:src=>"#{asset_path 'mail.png'}"}
I don't even know how I'd get the body preloaded in there. I know there is a :body declaration but its usage in this context eludes me.
Thoughts?
The mail_to helper only appears to accept a block in Rails 4, not previous versions. If you're using Rails 3 and can’t upgrade, you could do something like this:
- mail_link_content = capture_haml do
%img{:src=>asset_path('mail.png')}
=mail_to "friend#example.com", mail_link_content
Note that you don’t need to use a string if the contents are all interpolated (you might need to add parentheses though).
To get the body content, you just need to pass an options hash as the last argument to mail_to with a :body key:
Rails 4:
= mail_to "friend#example.com", :body => "Body text here" do
%img{:src=>asset_path('mail.png')}
Rails 3:
=mail_to "friend#example.com", mail_link_content, :body => "Body text here"

Rails ajax:success callback not triggering in production

My ajax callbacks are not triggering in my production environment. However, they are trigering in development with no errors. I'll simplify things for the sake of discussion.
Let's say I have a link that uses remote: true:
<%= link_to "Add Foo", new_foo_path, remote: true, id: 'new-foo' %>
foos_controller.rb
class FoosController < ApplicationController
def new
#foo = Foo.new
render partial: 'form' if request.xhr?
end
end
Using Chrome's console, I bind to ajax:success:
$(document).on("ajax:success", "#new-foo", function() { console.log("success!"); });
In development this works fine; I get the "success!" message in the Chrome console.
In production, however, it does not. The request is being made, the response is the form partial, but the callback does not fire.
Any ideas?
PS. The following does not work either.
$("#new-foo").bind("ajax:success", function() { console.log("success!"); })
There is no changes to config/environments/production.rb.
EDIT: It turns out ajax:error is being triggered when I click the link in production.
$(document).on("ajax:error", "#new-foo", function() { console.log("error"); });
My production logs don't show any errors, and the network tab in Chrome developer tools is showing a 200 OK response with the partial as the body.
However, the Content-Type header is text/javascript in production but text/html in development. Why is the same code responding with text/javascript in production?
The problem was that the browser was trying to execute the response body as JavaScript since the Content-Type header being sent back was text/javascript rather than text/html.
There are a number of ways to fix this. I chose to use jQuery's $.ajax function to set the dataType of all my ajax calls. This will ask the server to send back text/html.
$.ajaxSetup
dataType: 'html'
However, there are a couple other ways to ask the server for a specific response. See this StackOverflow answer for details.
There is still a lingering question, though. Why was the same code sending back text/html in development but text/javascript in production?
In case Google brought you here (like it did to me) because ajax:success is not working for you in a slightly different situation - when you're using button_to instead of link_to, this might save you some head banging.
Assuming you have this modified ERB:
<%= button_to "Add Foo", new_foo_path, remote: true, class: 'new-foo' %>
This JS won't work:
$(document).on("ajax:success", ".new-foo", function() { console.log("success!"); });
This won't work since the class new-foo is rendered on the input tag, while the data-remote="true" is rendered on the form tag. The ajax:success event fires on the form tag as a result -- not the child input tag, and that's why the console.log will never get called.
To fix this, change the class to form_class in the ERB:
<%= button_to "Add Foo", new_foo_path, remote: true, form_class: 'new-foo' %>

Rails escapes HTML in my plain text mails

I am using the rails 3.2.5 ActionMailer to send plain text mails. Given I have a mail view like this:
message_from_user.text.erb:
Hi <%= #recipient.name %>,
You got the following message from <%= #sender.name %>:
<%= #message %>
When #message is "quotes & ampersands", then the plain text mail contains "quotes & ampersands". So it seems like rails just treats this as a HTML view and escapes any html in order to prevent cross site scripting. However this is a plain text mail. The extension is .text.erb and ActionMailer detectes this and sets the MIME to text/plain. So I never want to escape any html in it.
I have quite a few mail templates in my application, they are all plain text. I would consider patching all of them to include <%=raw #message%> or <%= #message.html_safe %> bad style - not very DRY.
I tried varios work-arounds that included money patching Erubis. None of them seem to work. I am looking for some patch or config option or anything to disable escaping html for all .text.erb files.
Any help is greatly appreciated!
After some hours of debugging through the Erubis code, I found the following fix. You can just put it into config/initializers/fix_my_mails.rb. I've tested this with rails 3.2.7. It may work with other versions.
module ActionView
class Template
module Handlers
class ERB
def call(template)
if template.source.encoding_aware?
# First, convert to BINARY, so in case the encoding is
# wrong, we can still find an encoding tag
# (<%# encoding %>) inside the String using a regular
# expression
template_source = template.source.dup.force_encoding("BINARY")
erb = template_source.gsub(ENCODING_TAG, '')
encoding = $2
erb.force_encoding valid_encoding(template.source.dup, encoding)
# Always make sure we return a String in the default_internal
erb.encode!
else
erb = template.source.dup
end
self.class.erb_implementation.new(
erb,
:trim => (self.class.erb_trim_mode == "-"),
:escape => template.identifier =~ /\.text/ # only escape HTML templates
).src
end
end
end
end
end
It just disables HTML entities in every erb file containing .text in the file name.
Try
<%= #message.html_safe %>
You'd found this answer if you had used the search function. If that doesn't suit your needs, maybe check
https://rails.lighthouseapp.com/projects/8994/tickets/4858-actionmailer-is-html-escaping-ampersand-in-urls-in-plain-text-messages
If you haven't seen that yet, some options are discussed there

Resources