Attach Prawn pdf to email - ruby-on-rails

I have been searching for ages and I still cant seem to figure this out,
I am currently using the prawn gem and I want to be able to attach a pdf to an email in my invoice controllers create action. I am currently able to link to a generated pdf from my invoices/show page through http://localhost:3000/invoices/297.pdf but I cant figure out how to attach this pdf to an email.
Currently I am not storing the generated PDF anywhere and my mailer looks like this
Mailer
class InvoiceMailer < ActionMailer::Base
default :from => "notifications#example.com"
def invoice_email(user)
#user = user
mail(:to => user.email, :subject => "Invoice Recieved")
end
end
And my InvoicesController Create Action
{...}
respond_to do |format|
if #invoice.save
InvoiceMailer.invoice_email(#invoice.user).deliver
format.html { redirect_to invoice_url(#invoice, back_path:
{...}
How can I add my invoice as an attachment to this mailer? Do I need to store the invoice somewhere before I can send it?

How you do this depends on how long the PDF generation takes and/or how much load it places on your server and whether you care about that. In my case I was generating PDFs from user-generated-content and I was seeing some PDF creation times up in the 30+ seconds range. Solving for that becomes a run-the-job-somewhere-else and cache-it (whether DB or cloud storage) issue.
#toddmetheny is quite right in suggesting cloud storage for all but the simplest solutions. It gets more interesting if you are hosting on something like Heroku with ephemeral storage, or if you are separating PDF creation from email sending from user requests (e.g. from Heroku again, web dynos vs worker dynos). You can generate the PDF to a local temporary file, but that temporary file may not be there by the time you need to read it in your Mailer running on a 'worker'.
Really simple option
In your Mailer you could generate the PDF to a local file, read it back into memory, then attach it:
def invoice_email(user)
#user = user
attachments['filename_for_user.pdf'] = generate_pdf_content
mail(:to => user.email, :subject => "Invoice Recieved")
end
private
# I had troubles trying to get Prawn to generate 'in memory'
# so just write to, then read, then unlink a tempfile
def generate_pdf_content
pdf = some_method_that_generates_a_prawn_pdf_object
Tempfile.create do |f|
pdf.render_file f
f.flush
File.read(f)
end
end
I suggest you start here to get things working.
More complicated option
Someday you may want to separate the job that generates the PDF (which can take a long time) from the jobs that send email, which are usually much faster. The way I do this is to have a job that generates the PDF to a local Tempfile, then uploads that file to S3 storage and records the S3 object id (I do it in my database, you could just do it as a job attribute you push around).
When complete, that job creates a new mailer job. The mailer job (which may execute on a different server) downloads the PDF from S3 storage to a local file, then adds it to the email much like the simple option above.

You'll need a url you can work with. Any cloud storage solution is an option if you don't want to store it on your database.
Here's some pertinent info on adding attachments to mailers from rails guides on action mailer:
2.3.1 Adding Attachments
Action Mailer makes it very easy to add attachments.
Pass the file name and content and Action Mailer and the Mail gem will automatically guess the mime_type, set the encoding and create the attachment.
attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
When the mail method will be triggered, it will send a multipart email with an attachment, properly nested with the top level being multipart/mixed and the first part being a multipart/alternative containing the plain text and HTML email messages.

Related

Creating pdf and sending it via email

I am working on a phone billing application.
I have created simple Rails App with 3 tables Client, Invoice, CallRecord.
I can browse my dummy data and all works fine.
I would like to add a feature to send invoices to clients via email.
I was thinking about creating a simple link/button on the invoice#show view: Send invoice.
I was looking at WickedPDF gem as it will generate pdf from HTML.
I have three questions:
1) Is WickedPDF good for what I am looking for?
I am not trying to serve PDF to a user browsing the website. I don't want to make it downloadable.
I just need to generate it and save it when the Send Invoice is clicked, if it hasn't been generated already. And then email it using rails mailer to a client keeping the generated copy on the server(all this will be done in a custom controller action).
2) Where would be the best place to store this generated pdf's? Custom folder in Assets folder, public folder?
3)How would you go about saving files using rails? I have done a bit of rails but I have never done anything this direction.
You can actually use Wicked-PDF directly without the
controllers by:
pdf = WickedPdf.new.pdf_from_string('<h1>Hello There!</h1>')
or
WickedPdf.new.pdf_from_string(
render_to_string('templates/pdf.html.erb', :layout => 'pdfs/layout_pdf'),
:footer => {
:content => render_to_string(:layout => 'pdfs/layout_pdf')
}
) # which is awesome
Storing them should be in the public folder in a similar format:
public/users/:user_id/:invoice_id
you should consider doing that as a delayed job using sidekiq or something because the generation would take time and the user should not wait until it is generated.

Email open notification - ruby on rails

If I will send 100 email to the registered user and I want to know if users open email or not
How can I do this using Ruby on Rails?
The only way to do this, is to use html email with a tracker image. You need to include a user specific image into the code.
class TrackingController < ApplicationController
def image
# do something with params[:id]
send_file "/path/to/an/image"
end
end
add the following route:
# Rails 2
map.tracking_image "tracking_image/:id.gif", :controller => 'tracking', :action => image
# Rails 3
match 'products/:id', :to => 'tracking#image', :as => "tracking_image"
# Rails 4 (match without verb is deprecated)
get 'producsts/:id' => 'tracking#image', as: 'tracking_image'
# or
match 'producsts/:id' => 'tracking#image', as: 'tracking_image', via: :get
in your email template something like this:
<%= image_tag tracking_image_url(#user.id) %>
But be aware, that this it's not guaranteed that the user reads the email and loads the image, some email clients don't load images, until the user wants to. And If he doesn't you can't do anything about this. Also if the user uses text mail only this won't work neither.
Short answer, You can't. Slightly longer answer You can't reliably.
Using something like VERP you can automate the the bounce processing, to get a fairly good idea if the far end mail server accepted the email. But after that all bets are off. You can't really tell what the email server did with it, (route it to junk/spam folder, put in inbox, silently drop it on the floor/bit bucket, etc..). You could enable read-receipt headers in your email, but that is client specific (and people like me eat/deny them). You can look into using a web bug, for example customize each email with an HTML file, that pulls a remote image, that has a unique id associated with it, but again client specific, most will not load remote images. So unless the email bounces there is no 100% reliable way to tell what happens to the email after it leaves your server.
I am not very familiar with ruby but have written multiple mass mailer apps. You can use a webbug image to get an approximate open rate. Basically it is just a one pixel or transparent image with some tracking information:
<img src="http://mysite/trackingimage.gif?email=x&customer=y">
What I do is make a directory called trackingimage.gif with an index in it that reads and stores the url params and then relocates to the real image.

Rails: Email a pdf generated with prawn to an email sent by ActionMailer?

I have an ecommerce app. I'm using Prawn to generate pdf invoices of orders. I'm using a standard Prawn setup. In views/admin/orders, I have a file called show.pdf.prawn. When the seller is viewing an order in his admin section, he clicks a link that opens the pdf version of the orders/show view. This all works perfectly.
Now, the tricky part. When an order is completed, I send an email to the seller. What I'd like to do is attach the pdf invoice version of orders/show to that email. Is it possible to do this? The documentation on email attachments is pretty limited and I haven't been able to find resources that go through the workflow that I'm describing.
Any guidance is appreciated.
Sending an attachment with an email is fairly easy with ActionMailer:
class InvoiceMailer < ActionMailer::Base
def email_with_attachment(pdf_invoice)
.
.
.
attachment "application/pdf" do |a|
a.filename = "some_invoice.pdf"
a.body = pdf_invoice
end
end
end
One problem you might have with this is generating the pdf file outside of the prawnto method (when using the prawnto plugin)-
If this is is the case I strongly recommend you to use this approach instead.
I had the same problem, i managed to do it by generating the pdf from a model, much easier than evaluating the template. I replied with the answer here :
Save a Prawn PDF as a Paperclip attachment?

How to store private pictures and videos in Ruby on Rails

Here's a story:
User A should be able to upload an image.
User A should be able to set a privacy. ("Public" or "Private").
User B should not be able to access "Private" images of User A.
I'm planning to user Paperclip for dealing with uploads.
If I store the images under "RAILS_ROOT/public/images", anyone who could guess the name of the files might access the files. (e.g., accessing http://example.com/public/images/uploads/john/family.png )
I need to show the images using img tags, so I cannot place a file except public.
How can I ensure that images of a user or group is not accessible by others?
(If I cannot achieve this with Paperclip, what is a good solution?)
You may make your rails server output the contents of image files. This is done via a controller action (most of actions print HTML, but this one will print JPG, for example).
Then you may use your authorization system to restrict access on controller level!
class ImagesController
#Default show Image method streams the file contents.
#File doesn't have to be in public/ dir
def show
send_file #image.filename, :type => #image.content_type,
:disposition => 'inline'
end
# Use your favorite authorization system to restrict access
filter_access_to :show, :require => :view, :attribute_check => :true
end
In HTML code you may use:
<img src="/images/show/5" />
I would have Paperclip use S3 on the back-end, set uploaded files to private, and then use "Query String Request Authentication Alternative" to generate the URLs for my image tags.
http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAuthentication.html
Here's how I did this in a similar application.
Store your images on Amazon S3 instead of the local file system. Paperclip supports this.
Set your :s3_permissions to "private" in your Paperclip options
In your Image model, define a method that let's you output an authorized, time-limited url for the image.
Mine looks like this:
def s3_url(style = :original, time_limit = 30.minutes)
self.attachment.s3.interface.get_link(attachment.s3_bucket.to_s, attachment.path(style), time_limit)
end
You can then show images to people only if they're authorized to see them (implement that however you like)–and not have to worry about people guessing/viewing private images. It also keeps them from passing URLs around since they expire (the URL has a token in it).
Be warned that it takes time for your app to generate the authorized urls for each image. So, if you have several images on a page, it will affect load time.
If you want to host files yourself, you can perform authentication at the controller level as has been suggested. One of my applications has an AssetController that handles serving of files from the 'private' directory, for example.
One thing I wanted to add is that you should review this guide for setting up X-Sendfile, which will let your application tell the web server to handle actually sending the files. You'll see much better performance with this approach.

how to attach files that are stored in the database to the action mailer?

you have mention about how to store files in database.
How to attach those files with mail using action mailer?
I have search many sites but not able to find the way to attach database files.
Everywhere the help of attaching files stored in file system is given.
It's not very different from sending attachments stored on disk.
Say you have a model Binary that corresponds to files in the file system. If it responds to content_type and data, then something like this should work:
class AttachmentMailer < ActionMailer::Base
def attachment(recipient, binary)
recipients recipient
subject "Your requested file"
from "example#example.com"
attachment :content_type => binary.content_type, :body => binary.data
end
end
# Wherever you want to e-mail something:
binary = Binary.find(:first)
Notifier.deliver_attachment("user#example.com", binary)
Of course, if you store your data differently or if the database columns are named differently, you should adjust the methods of the Binary class (or whichever class you use) in the above example.
I hope this helps.

Resources