Preview emails in rails with text and html - ruby-on-rails

In our Rails 3.2 app we have the following set up to preview emails:
examples_controller
def registration
mail = EventMailer.registration(registration: EventRegistration.last, cache_email: false)
return render_mail(mail)
end
event_mailer.rb
def registration(options = {})
#registration = options[:registration]
#event = Event.find(#registration.event_id)
#subject = options[:subject] || 'Your Learn Registration'
return_email = mail(to: #registration.email, subject: #subject)
# the email if we got it
return_email
end
We just added a text version of the emails, so now in app/views/event_mailer we have registration.html.erb and registration.text.erb. Before adding the text version of the email users could browse to /webmail/examples/registration and view the html version of the email. After adding the text email, that is broken.
Ideas on how to fix this? I tried setting multipart to false in the examples controller, but that did not work.
Also, here is the render_mail method...
def render_mail(mail)
# send it through the inline style processing
inlined_content = InlineStyle.process(mail.body.to_s,ignore_linked_stylesheets: true)
render(:text => inlined_content, :layout => false)
end

I think it's not easy to debug all this code with just seeing some parts, so I'm going to suggest a different solution.
Some time ago I wrote a gem to preview emails (attachments and multiparts as well) in the browser: https://github.com/markets/maily
Main features:
Visual preview via browser (multiparts and attachments)
Template edition (development)
Email delivery
Flexible authorization system
Easy way to hook sample-data for emails
It's a Rails mountable (by default under /maily) engine, so you just need to install it, configure it and you'll be able to preview your email templates in your browser.
You can try to integrate Maily and you'll get a lot of things done, ping me if you have any doubt or suggestion.

Related

In Rails 5 how to use wicked pdf and mailgun together?

Using the mailgun API is slightly different from using ActionMailer.
Often I see something like this:
attachment['...'] = ...
in the solutions I found..
but what I am trying to build with mailgun currently looks like this:
def print
#invoice = Invoice.find(params[:id])
#order = #invoice.order
#customer = #order.customer
pdf = render_to_string(:pdf => 'file_name',
:template => 'invoices/invoice.pdf.erb',
:layout => 'invoice_pdf.html')
mg_client = Mailgun::Client.new 'key-xxxxxx'
mb_object = Mailgun::MessageBuilder.new
mb_object.add_attachment pdf
mg_client.send_message 'mg.mydomain.com', mb_object
end
And when I press the "print" button in my application (the pdf is meant to be send to a printer), then I get:
string contains null byte
(highlight!) mb_object.add_attachment pdf
Apparently the pdf object is not really something.
How could this be?
EDIT 1
Maybe it's better to ask, since this also needs to be done: How do I save from wicked pdf to S3 (in as few steps as possible).

Sending mail, getting ActionView::MissingTemplate <path> with 'mailer'

When trying to send emails with Sidekiq, I am getting this error
ActionView::MissingTemplate: Missing template campaign_mailer/hugh_email with "mailer". Searched in: * "campaign_mailer"
In the controller
CampaignMailer.hugh_email(email, user).deliver_later
In campaign_mailer.rb
layout 'mailer'
def hugh_email(email, user)
mail(to: #user.email, subject: email.subject)
end
app/views/campaign_mailer/hugh_email.html.slim has the html, and I have a template in layouts/mailer
Note: I have tried everything I can find, even re-generated mailers. It seems to work with a new mailer when I send with deliver_now, but as soon as I try deliver_later the error returns (even when going back to trying a deliver_now).
UPDATE:
My solution to this probably won't help many people, but I'll add it here just in case.
I now save the emails on my system, and pass the html into the mailer with CampaignMailer.hugh_email(email.subject, html).deliver_later. In the campaign_mailer.rb I run
mail(to: to_address, subject: email_subject) do |format|
format.text { render(text: "") }
format.html { html }
end
Again, unfortunately this probably won't help most people having this issue. But I essentially solved the problem by specifying the html instead of using the mailer view.
hugh_email.html.slim must be at
/app/views/campaign_mailer/hugh_email.html.slim
mailer.html.slim at
/app/views/layouts/mailer.html.slim
mailer.text.erb at
/app/views/layouts/mailer.text.erb

Rails 4 - Postmark integration

I'm trying to figure out how to send transactional email from my Rails 4 app.
I have found tutorials for the postmark gem, but I'm struggling to close the gaps between what's assumed in the tutorials (where to do the suggested steps!) and what I know.
I have installed both the ruby and the rails gems in my gemfile:
gem 'postmark-rails', '~> 0.13.0'
gem 'postmark'
I have added the postmark config to my config/application.rb:
config.action_mailer.delivery_method = :postmark
config.action_mailer.postmark_settings = { :api_token => ENV['POSTMARKKEY'] }
I want to try to make and use email templates in postmark.
The instructions in the postmark gem docs say I need to:
Create an instance of Postmark::ApiClient to start sending emails.
your_api_token = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
client = Postmark::ApiClient.new(your_api_token)
I don't know how to do this step? Where do I write the second line? I have my api token stored in my config. I don't know how to make an instance of the postmark api client.
Can anyone point me to next steps (or a more detailed tutorial)?
After you have installed the gems, you need to create a Mailer. I presume you have already configured the API keys etc. in the correct manner, so I will concentrate on actually sending out a templated / static email.
Lets create app/mailers/postmark_mailer.rb file with the following contents.
class PostmarkMailer < ActionMailer::Base
default :from => "your#senderapprovedemail.com>"
def invite(current_user)
#user = current_user
mail(
:subject => 'Subject',
:to => #user.email,
:return => '74f3829ad07c5cffb#inbound.postmarkapp.com',
:track_opens => 'true'
)
end
end
We can then template this mailer in the file app/views/postmark_mailer/invite.html.erb Let's use the following markup to get you started.
<p>Simple email</p>
<p>Content goes here</p>
You can write it like any other .html.erb template using tags, HTML and alike.
To actually send this email out you need to place an action in your controller, in the following manner.
PostmarkMailer.invite(current_user)
Alternatively, if you want this email to be sent upon visiting homepage, it would most likely look like this:
app/controllers/home_controller.rb with content
class HomeController < ApplicationController
# GET /
def index
PostmarkMailer.invite(current_user)
end
end
and corresponsing route
config/routes.rb with content
root :to => 'home#index'
I hope this answers your question.

button to save current page in rails 3.2

I need to have a button to save the current web site (just like clicking on "Save as"), I created a method in the controller which works great for any external site (like http://www.google.com) but doesn't work for the sites inside my application, I get a timeout error!. This has no explanation to me :(
Any clue what is the issue?
#CONTROLLER FILE
def save_current_page
# => Using MECHANIZE
agent = Mechanize.new
page = agent.get request.referer
send_data(page.content, :filename => "filename.txt")
end
I tried also Open URI, same problem!
#CONTROLLER FILE
def save_current_page
# => USANDO OPEN URI
send_data(open(request.referer).read, :filename => "filename.txt")
end
I'm using rails 3.2 and ruby 1.9, any help is appreciated, I already spent like 10 hours trying to make it work!!
Rails can only handle one request at a time. It's a never-ending standoff between the two requests - the first request is waiting for the second request, but the second request is waiting for the first request, and therefore you get a Timeout error. Even if you're running multiple instances of the app with Passenger or something, it's a bad idea.
The only way I can think to get around it would be to use conditional statements like so:
referer = URI.parse(request.referer)
if Rails.application.config.default_url_options[:host] == referer.host
content = "via yoursite.com"
else
agent = Mechanize.new
page = agent.get request.referer
content = page.content
end
send_data content, filename: "filename.txt"
A little dirty but it should get around the Timeout problem. As far a getting the actual content of a page from your own site - that's up to you. You could either render the template, grab something from cache, or just ignore it.
A much better solution would be to enqueue this code into something like Resque or Delayed Job. Then the queue could make the request and wait in line to request the page like normal. It would also mean that the user wouldn't have to wait while your application make a remote request, which is dangerous because who knows how long the page will take to respond.
After several hours and lots of other posts I got to a final solution:
Bricker is right in that it is not possible for rails to render more than once in a call, as taken from http://guides.rubyonrails.org/layouts_and_rendering.html "Can only render or redirect once per action"
The site also states "The rule is that if you do not explicitly render something at the end of a controller action, Rails will automatically look for the action_name.html.erb template in the controller’s view path and render it."
Then, the solution that worked great for me was to tell the controller to render to a string if a download flag (download=true) was set in :params (I also use request.url to have it working from any view in my application)
View:
= link_to 'Download', request.url+"&downloadexcel=true", :class => 'btn btn-primary btn-block'
Controller:
def acontrolleraction
#some controller code here
if params[:downloadexcel]
save_page_xls
else
# render normally
end
end
def save_page_xls
#TRESCLOUD - we create a proper name for the file
path = URI(request.referer).path.gsub(/[^0-9a-z]/i, '')
query = URI(request.referer).query.gsub(/[^0-9a-z]/i, '')
filename = #project_data['NOMBRE']+"_"+path+"_"+query+".xls"
#TRESCLOUD - we render the page into a variable and process it
page = render_to_string
#TRESCLOUD - we send the file for download!
send_data(page, :filename => filename, :type => "application/xls")
end
Thanks for your tips!

Integrate premailer gem with Rails 2.X application

I have a rails 2.3 application and would like to integrate the premailer gem to it.
I found how can you do it for a rails 3.X application:
How to Integrate 'premailer' with Rails
Anyone knows how to do it for action mailer 2.3.10?
I've spent a good chunk of the last couple of days on this now and it seems there is no great solution. It is possible to render the message explicitly and then pass the result through Premailer, but it gets messy in combination with multipart emails and HTML layouts and if the template uses some other encoding than ASCII-8BIT.
In a straight HTML email without multiparts and assuming an ASCII-8BIT encoded template, this works for me:
def some_email
recipients "Reciever <reciever#example.com>"
from "Sender <sender#example.com>"
subject "Hello"
content_type "text/html"
message = render_message("some_email", { }) # second argument is a hash of locals
p.body = Premailer.new(message, with_html_string: true).to_inline_css
end
However, if the template is encoded with some other encoding than ASCII-8BIT, Premailer destroys all non-ASCII characters. There is a fix merged into the Premailer repo, but no version has been released since. Using the latest revision and calling Premailer.new(message, with_html_string: true, input_encoding: "UTF-8").to_inline_css or similar should work. The merge commit is https://github.com/alexdunae/premailer/commit/5f5cbb4ac181299a7e73d3eca11f3cf546585364.
In the case of multipart emails I haven't really gotten ActionMailer to use the correct content types internally for rendering the templates. This results in the implicit typing via template file names not working and, as a result, layouts being incorrectly applied to text versions. A workaround for this would be to explicitly use no layout for the text version, resulting in something like this (note the template names):
def some_multipart_email
recipients "Reciever <reciever#example.com>"
from "Sender <sender#example.com>"
subject "Hello"
content_type "text/html"
part "text/html" do |p|
message = render_message("some_email_html", { })
p.body = Premailer.new(message, with_html_string: true).to_inline_css
end
part "text/plain" do |p|
p.content_type = "text/plain"
p.body = render(file: "some_email_text", body: { }, layout: false)
end
end

Resources