Sending an html attachment with ActionMailer 3 - ruby-on-rails

I am trying to send an email which contains a single HTML attachment. The problem is that the attachment html is appearing in the body of the email, and the view is showing up as the attachment. Pretty much the opposite of what I expected.
I am able to send attachments of other types properly, but when trying to send a single attachment that is HTML, it consistently is displayed rather than 'attached'.
class Notifier < ActionMailer::Base
default :from => "from#example.com"
def welcome(email)
attachments['this is an html file.html'] = "<b>yeah this is html!</b>"
mail(:to => email, :subject => "Attempting an attachment")
end
end
And my app/views/notifier/welcome.html.erb
Hi there! This is <b>html</b> within a view
The resulting email looks like this:
(notice the attachment html is actually displayed in the body of the email)

This is a feature of many email clients that if they can display attachments inline they will, so the user doesn't have to open a separate program just to see what's inside. To be honest though it's more meant for images.
If you want to send an html file, you're going to need to add it to a zip or other archive and send that instead.

Related

Contact Form for User Profile

I been struggling of thinking of a way to complete this function.
Currently I have a user with a profile. And a general Contact form model that is table-less and doesn't save anything to the database.
My goal is to have a general contact form, In which I can link a contact button on a individual user profile. That contact form when submitted will be sent to the user email specified in the profile attribute. So for example the profile has a field t.string contact_email.
Currently I have the contact model set up where it can send to one individual email. Mainly the app owner.
class ContactMailer < ApplicationMailer
default :to => "stephen#example.com"
def contact_me(msg)
#msg = msg
mail from: #msg.email, subject: #msg.subject, body: #msg.content
end
end
My goal is to simply link the
default :to => "stephen#example.com"
to something like
default :to => "#profile.contact_email"
I have no idea how to specify the user for the form or if its possible. The process would have to include once the visitor hits contact us the form takes the profile email and uses it as the recipient.
Sorry if this seems like I brung nothing to table to answering I'm just looking for a tip on maybe where to start or how it could be done.
Note: Don't get into this thing that you would have to specify to in default. You can simply send to in mail method like you are sending from, subject and other variables.
There are several ways to do it:
1) You can use hidden_field_tag to pass along the email of that user like following
<%= hidden_field_tag :contact_email, #user.contact_email %>
And then on server side, you can have its value through parmas[:contact_email].
2) You can send the email of the user when you can call contact_me. Right now, you are only sending one parameter msg. So,
def contact_me(msg, to)
#msg = msg
mail to: to, from: #msg.email, subject: #msg.subject, body: #msg.content
end
And when you call contact_me, you can do: ContactMailer.contact_me(msg, #user.contact_email) though it may be different depending upon the actual implementation in your code, but the concept would always be same.

How to modify actionmailer email html_part before sending

I have everything at the point where I'm about to send out the email but I need to modify all links to include Google Analytics attributes. The problem is that if I try and read/write the html_part.body of the email, the entire html string somehow becomes encoded and doesn't display the email properly (i.e. <html> becomes <html>). I have logged the html_part.body.raw_source in the logger and it shows as proper unencoded HTML, it's only when the email is actually sent does the encoding occur.
EBlast.rb (ActionMailer)
def main(m, args={})
# Parse content attachment references (they don't use helpers like the layout does)
# and modify HTML in other ways
m.prep_for_email self
#email = m # Needed for helper methods in view
mail_args = {
:to => #user.email,
:subject => m.subject,
:template_path => 'e_blast',
:template_name => 'no_template'
}
mail_args[:template_name] = 'main' if m.needs_template?
m.prep_for_sending mail(mail_args)
end
Email.rb
def prep_for_sending(mail_object)
if mail_object.html_part
# If I simply do a 'return mail_object', the email sends just fine...
# but the url trackers aren't applied.
# Replace the content with the entire generated html
self.content = mail_object.html_part.body.decoded
# Add Google analytics tracker info to links in content
apply_url_tracker :source => "Eblast Generator", :medium => :email
# Replace the html_part contents
mail_object.html_part.body = content
# At this point, mail_object.html_part.body contains the entire
# HTML string, unencoded. But when I send the email, it gets its
# entities converted and the email is screwed.
end
# Send off email
mail_object
end
Looks like I'm answering my own question again - I'm on a roll this week.
Apparently setting the body directly creates some odd attribute called 'body_raw' instead of replacing the raw_contents of the html_part. So basically I ended up having a duplicate part embedded in the mail object (I don't know why it does this). Creating a separate Mail::Part and assigning it to html_part just added another part instead of replacing html_part! WTF?!
New Edit: Scratch my last remark about String.replace. It looked like it was working but when I went to another computer and tested it, the same problem of duplication occurred.
Another Edit: Finally?
Before I executed the apply_url_tracker method I had reset the content of the email (for the purposes of changing all the links in the rendered view). I don't have any idea why that screws with the Mail object considering the message should already have been rendered but changing my methodology to the following has fixed the duplication of email parts and their subsequent 'reencoding'. I no longer change the content attribute, I only change the html_part:
def prep_for_sending(message)
if message.html_part
# Replace the html raw_source
message.html_part.body.raw_source.replace apply_url_tracker(message.html_part.body.decoded, :source => "Eblast Generator", :medium => :email)
end
message
end
Clarification:
Even though the call to mail() produces a Mail object with fully rendered HTML/Text parts (i.e., fully rendered views), changing the attribute that is USED by those views (in my case, the 'content' attribute) screws up the final send. Don't modify your model before sending, JUST MODIFY THE MAIL PART DIRECTLY.

PDF attachment in email is called 'Noname'

When I send an email with a attached pdf file the email only shows a file called 'Noname'. The file itself is the multipart section of the email with the base64 attached pdf. How can I send the PDF so it comes up as an attachment and doesn't corrupt the email?
Here is my code:
attachments['126539_statistics.pdf'] = File.read("app/assets/pdfs/126539_statistics.pdf")
mail(:to => email, :subject => subject, :body => message, :content_type => 'application/pdf')
I had face same problem. I have corrected it by following way
1) As per ActionMailer Guide, You need to make view file into app/views/[action_mailer_model_name]/[method_name]
Here is reference of guide: http://api.rubyonrails.org/classes/ActionMailer/Base.html
2) Action Mailer is smart enough that it identify content-type automatically while reading file. So there is no need to pass explicitly 'application/pdf' in your mail function.
Hope, this information helps to solve your problem
Also experienced the same problem
solution is :
set Attachments before sending mail function.
code is attached below:
attachments["invoice_#{invoice.bill_date.strftime('%B-%Y')}.pdf"] = WickedPdf.new.pdf_from_string(
render_to_string pdf: 'project_report.pdf',
template: 'users/_invoice.html.erb'
)
mail(to: email , from: from, subject: subject) do |format|
format.html
end

Why do I have to specify text format to render template with ActionMailer?

I'm using ActionMailer 3.0.7
According to the docs plain text emails are the default. So if I have an EnquiryNotifier mailer with a notify method then I expect that app/views/enquiry_notifier/notify.text.plain.erb will be rendered.
If I simply use mail(someparams) within the notify method then the body of the email is empty.
I read that ActionMailer is meant to scan the view directory to look for all types of templates.
However, if I specify the format within a block and do
mail(:to => 'somebody', :subject => 'something') do |format|
format.text
end
then my template notify.text.plain.erb does get rendered.
Maybe unrelated: If I don't specify the format but rename the template to notify.erb then it works but the email is sent as text/html.
Here's what seems to work for me in rails 3.0.6:
I don't specify a format at all in my mailer class, I just let it find the view automatically.
I name my view "notify.text.erb"

rails mailer with different layouts

I use one layout for all my emails in my Notifier model (20+ emails)... however sometimes I just want to send a plain text email with no layout or html at all. I can't seem to be able to figure out how? If I try to send a plain text email i still get the layout, and all the HTML in the email.
I'm using Rails 2.3.8.
I read about this monkey patch here... but it seemed to indicate a newer version of rails had over come this? And I don't really wanna monkey patch if I can avoid one.
Rails - setting multiple layouts for a multipart email with mailer templates
layout "email" # use email.text.(html|plain).erb as the layout
def welcome_email(property)
subject 'New Signup'
recipients property.email
from 'welcome#test.com'
body :property => property
content_type "text/html"
end
def send_inquiry(inquire)
subject "#{inquire.the_subject}"
recipients inquire.ob.email
from "Test on behalf of #{inquire.name} <#{inquire.email}>"
body :inquire => inquire
content_type "text/plain"
end
I also have 2 files.
email.text.html.erb
email.text.plain.erb
It always uses text.html.erb... even if the content_type is "text/plain"
edit: Figured it out, the layouts follow a different naming scheme to the email templates. Just rename them as follows:
layout.text.html.erb => layout.html.erb
layout.text.plain.erb => layout.text.erb
I also made the mistake of manually defining the parts, if you use this:
part :content_type => 'text/plain',
:body => render_message('my_template')
Then Rails can't determine the content_type for your part and it assumes it's HTML.
After I changed those two things it worked for me!
original reply follows..
I've struggled with this question many times in the past, usually ending up with some sort of non-dry quick and dirty solution. I always thought I was the only one with this problem because Google turns up exactly nothing useful on the subject.
This time I decided to dig into Rails to figure it out but so far without much success, but maybe my findings will help someone else figure this out.
What I found was that in ActionMailer::Base the #render_message method is tasked with determining the proper content_type and should assign it to #current_template_content_type. #default_template_format then either returns the proper mime type for the layout or, if #current_template_content_type isn't set, it will default to :html.
This is what ActionMailer::Base#render_message looks like in my app (2.3.5)
def render_message(method_name, body)
if method_name.respond_to?(:content_type)
#current_template_content_type = method_name.content_type
end
render :file => method_name, :body => body
ensure
#current_template_content_type = nil
end
The trouble is that method_name appears to be a string (the name of the local view, in my case "new_password.text.html") and strings of course do not respond_to #content_type, meaning #current_template_content_type will always remain nil, and so the #default_template_format will always default to :html.
Not much closer to an actual solution, I know. The ActionMailer internals are just too opaque for me.
OK, not sure if this works, but it seems the default content_type is text/plain, so you would only need to set the content type if you want something other than text/plain.
Try this:
def send_inquiry(inquire)
subject "#{inquire.the_subject}"
recipients inquire.ob.email
from "Test on behalf of #{inquire.name} <#{inquire.email}>"
body :inquire => inquire
end
I still think you should consider this:
layout "email", :except => [:send_inquiry]
I would use the above because the plain text email does not seem to have a 'layout', only the actual content you want to send.
I found this that I think could be useful.
http://blog.blazingcloud.net/2009/11/17/simple-email-form-with-actionmailer/
He makes use of renaming the view templates for different content types.

Resources