I'm using wick_pdf in my Rails 4 app to convert several html templates into pdfs. I then attach these pdfs to an outbound email. The email and the pdfs are sending correctly, but there is an additional blank pdf (occasionally not completely) that gets sent along with my desired pdfs. I've attached my code below. Please let me know if you have any suggestions.
def approval_notification(id)
#user = User.find(id)
esign = WickedPdf.new.pdf_from_string(render_to_string pdf: "esign", template: "disclosures/pdfs/esign_pdf.html.erb", formats: :html, encoding: "UTF-8")
mail(:to => #user.email, :subject => 'Congratulations! You have been approved!') do |format|
format.html
format.pdf do
attachments['Electronic_Signature.pdf'] = esign
end
end
end
Update:
Alright, I managed to fix the issue. I still don't completely understand what was wrong so please explain if you know. The revised code is below:
def approval_notification(id)
#user = User.find(id)
esign = WickedPdf.new.pdf_from_string(render_to_string pdf: "esign", template: "disclosures/pdfs/esign_pdf.html.erb", formats: :html, encoding: "UTF-8")
attachments['Electronic_Signature.pdf'] = esign
mail(:to => #user.email, :subject => 'Congratulations! You have been approved!')
end
Related
Rails will helpfully send multipart email if there are multiple template types present (e.g. .txt and .html files for the same mailer action).
However, what if I want to do this without templates? Normally we specify body and content_type as arguments:
mail to: 'a#example.com', subject: 'Hello', body: 'Hi', content_type: 'text/html'
So how can this be achieved with multiple bodies having their own type?
class TestMailer < ActionMailer::Base
def welcome_email
mail(to: 'example#example.com', subject: 'Welcome to My Awesome Site') do |format|
format.html { render html: '<h1>Welcome XYZ!</h1>'.html_safe }
format.text { render plain: 'Welcome XYZ' }
end
end
end
To use it call: TestMailer.welcome_email.deliver_now.
Documentation, section 2.4
In rails 4, I am using gem 'wicked_pdf', '1.0.3' and
gem 'wkhtmltopdf-binary', '0.9.9.3' for pdf generation. I am downloading it as a file,
respond_to do |format|
format.html
format.pdf do
render :pdf => "#{#user.id}_file",
:disposition => "attachment",
:save_to_file => Rails.root.join('public/download_pdfs', "#{#user.id}_file.pdf"),
:layout => false
end
end
Here, I am sending this file via mail by adding this line in controller Email.deliver_sample_file_mail(#user, "#{Rails.root}/public/download_pdfs/#{#user.id}_file.pdf")
In mailer,
def send_invoice_pdf_mail(user, file_path)
filename = file_path.split("/").last
attachments["#{filename}"] = File.read(file_path)
mail(:to =>user.email, :subject => "File has been sent") do |format|
format.html { render :partial =>"/emails/users/sample", :locals=>{:user=>user}, :layout=>"email"}
end
end
Now the issue is both download file and email send is happening together.
How can I avoid file download feature? And how can I generate .pdf file so that it should send only to an email id?
You can save file as below without download
pdf = render_to_string pdf: "#{#user.id}_file", encoding: "UTF-8"
# then save to a file
save_path = Rails.root.join('public/download_pdfs','#{#user.id}_file.pdf')
File.open(save_path, 'wb') do |file|
file << pdf
end
Hope, this will Help you.
Controller method
def email_pdf
#job = Job.find_by_id(params[:id].to_i)
job_pdf = WickedPdf.new.pdf_from_string(render_to_string(:pdf => 'job', :template => 'job/show.pdf.erb', layout: 'mailer.html.erb'))
JobMailer.send_jobs_email(params[:id].to_i, 'vir.jain#gmail.com', 'Mahavir Jain', job_pdf).deliver_now
respond_to do |format|
format.json do
render :json => {:success => true}, :status => 200
end
end
end
Job Mailer
class JobMailer < ApplicationMailer
def send_jobs_email(job_id, email, name, job_pdf)
#name = name
#job = Job.find_by_id(job_id)
puts 'hello1'
attachments['job'] = job_pdf
puts #name
mail(to: email, subject: 'Job', from: 'info#janatrak.com', from_name: 'JanaTrak Admin')
end
end
Output
Rendered job/show.pdf.erb within layouts/mailer.html.erb (15.0ms)
"***************[\"/usr/local/bin/wkhtmltopdf\", \"-q\", \"file:///var/folders/dk/t3scf65x5vx23qq1fsm53s3r0000gn/T/wicked_pdf20151007-4801-h2de03.html\", \"/var/folders/dk/t3scf65x5vx23qq1fsm53s3r0000gn/T/wicked_pdf_generated_file20151007-4801-7uwh23.pdf\"]***************"
"***************[\"/usr/local/bin/wkhtmltopdf\", \"-q\", \"file:///var/folders/dk/t3scf65x5vx23qq1fsm53s3r0000gn/T/wicked_pdf20151007-4801-mp6b6i.html\", \"/var/folders/dk/t3scf65x5vx23qq1fsm53s3r0000gn/T/wicked_pdf_generated_file20151007-4801-3bayit.pdf\"]***************"
Rendered job_mailer/send_jobs_email.html.erb within layouts/mailer (0.1ms)
[{"email"=>"vir.jain#gmail.com", "status"=>"sent", "_id"=>"dc93087c39a949de827df06a1edb116e", "reject_reason"=>nil}]
Problem
From output, I can see, I am able to generate pdf properly but for some reason, it's not sending pdf as attachment. Also it's not sending email content. But when I remove attachment from code, it do properly send content of email.
I did checked
Rails 3 ActionMailer and Wicked_PDF
and tried both solution but none worked.
I'm using rails 4.2, ruby 2.1.2, wkhtmltopdf 0.12.2.1 (with patched qt).
Any help is really appreciated
***************** UPDATE ************************
Controller Method
def email_pdf
#job = Job.find_by_id(params[:id].to_i)
self.instance_variable_set(:#lookup_context, nil)
self.instance_variable_set(:#_lookup_context, nil)
job_pdf = WickedPdf.new.pdf_from_string(render_to_string(:template => 'job/show.pdf.erb', layout: 'mailer.html.erb'))
#job_pdf = WickedPdf.new.pdf_from_string('<h1>Hello There!</h1>')
save_path = Rails.root.join('pdfs','job.pdf')
dir = File.dirname(save_path)
FileUtils.mkdir_p(dir) unless File.directory?(dir)
File.open(save_path, 'wb') do |file|
file << job_pdf
end
JobMailer.send_jobs_email(params[:id].to_i, 'vir.jain#gmail.com', 'Mahavir Jain', save_path).deliver_now
respond_to do |format|
format.json do
render :json => {:success => true}, :status => 200
end
end
end
Mailer Method
def send_jobs_email(job_id, email, name, job_pdf)
#name = name
#job = Job.find_by_id(job_id)
puts 'hello1'
attachments['job.pdf'] = File.read(job_pdf)
puts job_pdf
mail(to: email, subject: 'Job', from: 'info#janatrak.com', from_name: 'JanaTrak Admin')
end
PDF is generating properly & i can see saved pdf.. But again somehow actionmailer not attaching pdf as well as not displaying content..
Am I anything missing related to actionmailer attachment?
Our Rails (3.0.14) application has user profiles where users can choose whether they want to receive HTML formatted emails. To keep things DRY, I would like to only setup one set of templates for all mails (HTML) and then use my own String extension dehtml (basically strip_tags with some formatting modifications) on the text/plain part. Also, I would like to keep the mailer code DRY.
So far, our mailer methods look like this:
def signup_confirmation(user)
#user = user
mail(:to => #user.email, :subject => ..., ...)
end
1. DRY templates: If possible, I would like to avoid having to create 200 additional mail templates, and autocreate the text/plain part from the HTML template. This is the basic idea (dehtml is my own String extension):
def signup_confirmation(user)
#user = user
mail(:to => #user.email, :subject => ..., ...) do |format|
format.html
format.text { render(:file => 'signup_notification.html').dehtml }
end
end
However, this fails with a 'missing template' error. How do I tell Rails to use the HTML template in both cases? I tried appending :formats => :html and :handler => :html but this didn't help.
I don't have a solution here right now. Any ideas?
2: DRY mailer methods:
Since our users should be able to decide whether they want to have HTML or not, the above method will look something like
def signup_confirmation(user)
#user = user
attachments.inline["email-header.jpg"] = File.read(...) if #user.wants_html
mail(:to => #user.email, :subject => ..., ...) do |format|
format.html if #user.wants_html
format.text { render(:file => 'signup_notification.html').dehtml }
end
end
Altogether, this triples the LOC in each method. I would like to DRY this up (since it will have to be inserted into at least 200 mailer methods) as far as possible. One idea would be to write my own mail method (let's call it mymail) as something like
def mymail(user, p={})
attachments.inline["email-header.jpg"] = File.read(...) if user.wants_html
mail(p) do |format|
format.html if user.wants_html
format.text
end
end
(ignoring the above text template problem for now) and then change each call to mail to mymail, as in
def signup_confirmation(user)
#user = user
attachments.inline["email-header.jpg"] = File.read(...) if #user.wants_html
mymail(user, { :to => #user.email, :subject => ..., ... })
end
This works. But is it good practice? Where do I best put mymail - in a helper?
Any insights and recommendations welcome!
I did something very similar to your first solution a long time ago. I don't really remember why it hat to be the way it is, but this is working for me:
def my_mail
mail(:to => #user.email ...) do |format|
format.text { convert_html_to_plain(__method__) } # first text
format.html # then html
end
end
def convert_html_to_plain(method)
old_formats = self.formats
self.formats = ["html"]
rendered = render "#{method}", :layout => false
self.formats = old_formats
# strip tags, reformat, etc. from rendered
rendered << render(:partial => "plaintext_footer", :locals => {:user => #user}, :formats => [:text] )
end
I have been using Wicked_pdf to render a view as a PDF and actionmailer to send emails, but I can't get them to work together. I want to attach a PDF version of a certain view to an email using actionmailer and send it out by clicking a link or a button. I have a link_to command that sends out an email. Here is my controller that gets the email generated:
def sendemail
#user = User.find(params[:id])
Sendpdf.send_report(#user).deliver
redirect_to user_path(#user)
flash[:notice] = 'Email has been sent!'
end
Here is what I have in my actionmailer:
class Sendpdf < ActionMailer::Base
default :from => "myemail#email.com"
def send_report(user)
#user = user
attachment "application/pdf" do |a|
a.body = #Something should go here, maybe WickedPDF.new.something?
a.filename = 'MyPDF'
end
mail(:to => user.email, :subject => "awesome pdf, check it")
end
end
I have seen many questions and answers, most dealing with Prawn. It seems like there should be a simple answer to this. Can anyone help?
UPDATE I'm grateful for a suggestion to use as an alternative option in the answer below. However, I would really like to learn how to render a view as a PDF and attach it to my email. I am open to using something different like Prawn or anything else if I need to.
2 good ways to do this the way you want:
1: Create the pdf in the controller, then send that to the email as a param.
# controller
def sendemail
#user = User.find(params[:id])
pdf = render_to_string :pdf => 'MyPDF'
Sendpdf.send_report(#user, pdf).deliver
redirect_to user_path(#user)
flash[:notice] = 'Email has been sent!'
end
# mailer
def send_report(user, pdf)
#user = user
attachments['MyPDF.pdf'] = pdf
mail(:to => user.email, :subject => "awesome pdf, check it")
end
2: Create the pdf in the mailer directly (a little more involved, but can be called from a model)
def send_report(user)
#user = user
mail(:to => user.email, :subject => "awesome pdf, check it") do |format|
format.text # renders send_report.text.erb for body of email
format.pdf do
attachments['MyPDF.pdf'] = WickedPdf.new.pdf_from_string(
render_to_string(:pdf => 'MyPDF',:template => 'reports/show.pdf.erb')
)
end
end
end
There are 2 ways for it.
Either, you want the pdf to be embedded in the email you are sending, so that when the user downloads the pdf from the email, there is no request to the render new pdf action for your respective controller.
I don't know how to do this efficiently because I have never done this before.
Or, you just provide a link to the pdf in your email and when the user clicks on it, now the action for creating the pdf is called, and then the user can download it.
This way, if there is a lot of burden on the server for the downloading of the pdf's, you can redirect these requests somewhere else. In short, there is a huge scope for efficiency.
A sample code for the 2nd method(code provided was written by using PDFkit, so change accordingly):
class PdfsController < ApplicationController
def pdf
respond_to do |format|
format.pdf { render :text => wickedPDF.new( Pdf.find(params[:id]).content ).to_pdf }
end
end
...
end
Replace the Pdf.find(params[:id]).content as per your choice, and the to_pdf method, as per wickedPDF.
Then, you can simply pass the link for the pdf download in your email like this
<%= link_to "Download", pdf_pdf_path(pdf, :format => "pdf") %>
or whatever suits as per wickedPDF.