Ruby on Rails 3.2 Mailer, localize mail subject field - ruby-on-rails-3

I'm currently writing a mailer in RoR 3.2 that would send out mails that should be localized based on a users' language. I managed to render the correct localized views but I'm having some difficulties with some fields that require changing the locale (like the subject).
I've already read some posts that are against changing the locale before sending the email. The users have many different languages and that would mean changing my locale every time a user is sent an email.
I know that it would be possible to change the locale, send the email, change back the locale. This doesn't feel like the rails way. Is there a correct way of doing this?
Here's a snippet:
class AuthMailer < ActionMailer::Base
add_template_helper(ApplicationHelper)
default :from => PREDEF_MAIL_ADDRESSES::System[:general]
[...]
def invite(address, token, locale)
#token = token
#locale = locale
#url = url_for(:controller => "signup_requests", :action => "new", :token => token.key, :locale => locale)
mail(:subject => "Invitation", :to => address) do |format|
format.html { render ("invite."+locale) }
format.text { render ("invite."+locale) }
end
end
[...]
end
My views
auth_mailer
invite.en.html.erb
invite.en.text.erb
invite.it.html.erb
invite.it.text.erb
...
In short, in this case, I'd like to localize the :subject using the #locale, but not by running: I18n.locale = locale

It is OK to change the global locale temporarily. There is a handy I18n.with_locale method for that. Also ActionMailer automatically translates a subject.
class AuthMailer
def invite(address, token, locale)
#token = token
#locale = locale
#url = url_for(:controller => "signup_requests", :action => "new", :token => token.key, :locale => locale)
I18n.with_locale(locale) do
mail(:to => address)
end
end
end
In the locale:
en:
auth_mailer:
invite:
subject: Invitation

Rails 4 way:
# config/locales/en.yml
en:
user_mailer:
welcome:
subject: 'Hello, %{username}'
# app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
def welcome(user)
mail(subject: default_i18n_subject(username: user.name))
end
end
default_i18n_subject - Translates the subject using Rails I18n class under [mailer_scope, action_name] scope. If it does not find a translation for the subject under the specified scope it will default to a humanized version of the action_name. If the subject has interpolations, you can pass them through the interpolations parameter.

You should be able to pass a locale when you call I18n like so:
mail(:subject => I18n.t("app.invite.subject", :locale => locale), :to => address) do |format|
format.html { render ("invite."+locale) }
format.text { render ("invite."+locale) }
end
Remember that the locale variable needs to be a symbol.

Related

Paypal IPN on Rails app not being called

We are using Paypal Adaptive Chained payments to allow contributions on a crowd funding website. I've got everything working as far as making the payment and being returned to the 'return_url' after a successful payment (in the sandbox).
Paypals documentation on testing IPN in the Sandbox is sparse. Perhaps I missed a setting somewhere to enable IPN callbacks?
I would like to be able to use the IPN callback to verify a successful payment. I would like to see the callback JSON so I know which field to capture and compare with. I have found sample code, but the IPN callback doesn't seem to be called in the Sandbox. I've tried to output the params like so (also the puts statement isn't called):
****backers_controller.rb
class BackersController < ApplicationController
include ActiveMerchant::Billing::Integrations
def callback_ipn
puts "callback: ipn"
notify = PaypalAdaptivePayment::Notification.new(request.raw_post)
puts notify.to_yaml
end
def callback_return
puts "callback: return"
#backer = Backer.find(params[:id])
#reward = #project.rewards.find_by_id(#backer.reward_id)
#backer.callback
respond_to do |format|
format.html { render :layout => 'application_proj_back_blog' }
format.json { render json: #backer }
end
end
(The 'callback_return' action is working)
****backer.rb
class Backer < ActiveRecord::Base
include ActionDispatch::Routing::UrlFor
include Rails.application.routes.url_helpers
def purchase
project = Project.find(self.project_id)
default_url_options[:host] = 'localhost:3000'
proj_owner_email = User.find(project.user_id).email
recipients = [{:email => PRIMARY_EMAIL,
:amount => total_pledge,
:primary => true},
{:email => project.paypal_email,
:amount => project_owner_cut,
:primary => false}
]
response = GATEWAY.setup_purchase(
:action_type => "PAY_PRIMARY",
:return_url => callback_return_project_backer_url(project, self),
:cancel_url => callback_cancel_project_backer_url(project, self),
:ipn_notification_url => callback_ipn_project_backer_url(project, self),
:currency_code =>"USD",
:receiver_list => recipients
)
puts response.to_yaml
pay_key = response["payKey"]
pledge_transactions.create!(:action => "purchase", :amount => total_pledge, :response => response)
return response["payKey"]
end
Back in backers_controller.rb, 'def purchase' calls the purchase:
****backers_controller.rb
class BackersController < ApplicationController
def purchase
#backer = Backer.find(params[:id])
#backer.purchase
redirect_to (GATEWAY.redirect_url_for(#backer.purchase))
end
It turns out my authentication was preventing paypal from calling my ipn listener.

Rails Devise 2.0 own Log-out action

I use Devise and I want to do my logout action.
What I want to do is, that when I log out, I want to create a own JSON object to return. At this time, after I logt out, I get all my root articles.
How can I write my own destory action like I have found the create action?
class SessionsController < Devise::SessionsController
def create
resource = warden.authenticate!(:scope => resource_name, :recall => :failure)
return sign_in_and_redirect(resource_name, resource)
end
def sign_in_and_redirect(resource_or_scope, resource=nil)
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource ||= resource_or_scope
sign_in(scope, resource) unless warden.user(scope) == resource
return render :json => {:success => true, :redirect => stored_location_for(scope) || after_sign_in_path_for(resource)}
end
def failure
return render:json => {:success => false, :errors => ["Login failed."]}
end
end
And my Routes in routes.rb
devise_for :users, :controllers => {:session => "sessions"} do
get "/users/sing_out" => "devise/sessions#destroy"
end
this is the destroy method of the sessions-controller.
you should be able to customize it to your needs. i think that it would be wiser to add another action and implementing your custom behavior there, as this will be less likely to cause unexpected errors with upgrading devise in the future.
# DELETE /resource/sign_out
def destroy
redirect_path = after_sign_out_path_for(resource_name)
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
set_flash_message :notice, :signed_out if signed_out
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request
respond_to do |format|
format.any(*navigational_formats) { redirect_to redirect_path }
format.all do
method = "to_#{request_format}"
text = {}.respond_to?(method) ? {}.send(method) : ""
render :text => text, :status => :ok
end
end
end

rails send empty message

Mailer:
class CustomerHistoryMailer < ActionMailer::Base
default :from => "notifications#example.com"
def log_email(to)
mail(:to => to,
:subject => 'Report')
end
end
Controller:
class Admin::ReportsController < ApplicationController
def index
CustomerHistoryMailer.log_email("me#example.com").deliver
end
end
View (app/views/customer_history_mailer/log_email.text.erb):
Hello, world!
In my mailbox I receive empty message with right subject. Why?
Is it possible that you have generated a .html.rb view as well, and rails is detecting both, thus sending a multipart message (being the HTML empty) thus you are not seeing anything because your email client defaults to the HTML view?
Can you verify that?
--- What about specifying the order for html/text in the multipart msg?
class UserMailer < ActionMailer::Base
def welcome_email(user)
#user = user
#url = user_url(#user)
mail(:to => user.email,
:subject => "Welcome to My Awesome Site") do |format|
format.html
format.text
end
end
end
I removed config.action_mailer.deprecation = :log line from config/environments/development.rb and it works now.

How can I customize Devise to send password reset emails using PostMark mailer

I'm trying to get all of my system's email notifications under one umbrella using PostMarkApp and utilizing the Rails gems (postmark-rails, postmark-gem, and mail). I have successfully created a mailer that handles sending receipts for purchases but I haven't been able to receive emails for forgotten passwords. My development logs show that Devise sent the message but no email is received in my inbox and the PostMark credits are not decremented.
What's the best or easiest way to have Devise's mailers send through my PostMark account?
Snippet from config/environments/development.rb
config.action_mailer.delivery_method = :postmark
config.action_mailer.postmark_settings = { :api_key => "VALID_API_KEY_WAS_HERE" }
config.postmark_signature = VALID_POSTMARK_SIGNATURE_WAS_HERE
My Mailer that uses Postmark
class Notifier < ActionMailer::Base
# set some sensible defaults
default :from => MyApp::Application.config.postmark_signature
def receipt_message(order)
#order = order
#billing_address = order.convert_billing_address_to_hash(order.billing_address)
mail(:to => #order.user.email, :subject => "Your Order Receipt", :tag => 'order-receipt', :content_type => "text/html") do |format|
format.html
end
end
end
EDIT: SOLUTION to my question is below
Solved it by having my Notifier mailer extend Devise::Mailer and specifying Devise to use my Notifier as the mailer within config/initializers/devise.rb
snippet from config/initializers/devise.rb
# Configure the class responsible to send e-mails.
config.mailer = "Notifier"
My Notifier Mailer now
class Notifier < Devise::Mailer
# set some sensible defaults
default :from => MyApp::Application.config.postmark_signature
# send a receipt of the Member's purchase
def receipt_message(order)
#order = order
#billing_address = order.convert_billing_address_to_hash(order.billing_address)
mail(:to => #order.user.email, :subject => "Your Order Receipt", :tag => 'order-receipt', :content_type => "text/html") do |format|
format.html
end
end
# send password reset instructions
def reset_password_instructions(user)
#resource = user
mail(:to => #resource.email, :subject => "Reset password instructions", :tag => 'password-reset', :content_type => "text/html") do |format|
format.html { render "devise/mailer/reset_password_instructions" }
end
end
end
Using the latest version of Devise, the methods above didn't help me. This is my solution.
In config/application.rb:
config.action_mailer.delivery_method = :postmark
config.action_mailer.postmark_settings = { :api_key => "your-API-key-here" }
In config/initializers/devise.rb:
config.mailer = "UserMailer" # UserMailer is my mailer class
In app/mailers/user_mailer.rb:
class UserMailer < ActionMailer::Base
include Devise::Mailers::Helpers
default from: "default#mydomain.com"
def confirmation_instructions(record)
devise_mail(record, :confirmation_instructions)
end
def reset_password_instructions(record)
devise_mail(record, :reset_password_instructions)
end
def unlock_instructions(record)
devise_mail(record, :unlock_instructions)
end
# you can then put any of your own methods here
end
Finally, make sure you have generated custom devise views
rails generate devise:views
and move the email templates from app/views/devise/mailer/ to app/views/user_mailer/
mv app/views/devise/mailer/* app/views/user_mailer/
If you also want to specify 'tags' in postmark headers you have to do this in your mailer:
# this override method is from Devise::Mailers::Helpers
def headers_for(action)
headers = {
:subject => translate(devise_mapping, action),
:from => mailer_sender(devise_mapping),
:to => resource.email,
:template_path => template_paths,
:tag => action.dasherize # specify the tag here
}
if resource.respond_to?(:headers_for)
headers.merge!(resource.headers_for(action))
end
unless headers.key?(:reply_to)
headers[:reply_to] = headers[:from]
end
headers
end
I also had to generate the views for devise and copy the mail templates into the right place for my mailer. Something like this -
rails generate devise:views
cp app/views/devise/mailer/* app/views/notification_mailer/

Rails 3 ActionMailer and Wicked_PDF

I'm trying to generate emails with rendered PDF attachements using ActionMailer and wicked_pdf.
On my site, I'm using already both wicked_pdf and actionmailer separately. I can use wicked_pdf to serve up a pdf in the web app, and can use ActionMailer to send mail, but I'm having trouble attaching rendered pdf content to an ActionMailer (edited for content):
class UserMailer < ActionMailer::Base
default :from => "webadmin#mydomain.com"
def generate_pdf(invoice)
render :pdf => "test.pdf",
:template => 'invoices/show.pdf.erb',
:layout => 'pdf.html'
end
def email_invoice(invoice)
#invoice = invoice
attachments["invoice.pdf"] = {:mime_type => 'application/pdf',
:encoding => 'Base64',
:content => generate_pdf(#invoice)}
mail :subject => "Your Invoice", :to => invoice.customer.email
end
end
Using Railscasts 206 (Action Mailer in Rails 3) as a guide, I can send email with my desired rich content, only if I don't try to add my rendered attachment.
If I try to add the attachment (as shown above), I get an attachement of what looks to be the right size, only the name of the attachment doesn't come across as expected, nor is it readable as a pdf. In addition to that, the content of my email is missing...
Does anyone have any experience using ActionMailer while rendering the PDF on the fly in Rails 3.0?
Thanks in advance!
--Dan
WickedPDF can render to a file just fine to attach to an email or save to the filesystem.
Your method above won't work for you because generate_pdf is a method on the mailer, that returns a mail object (not the PDF you wanted)
Also, there is a bug in ActionMailer that causes the message to be malformed if you try to call render in the method itself
http://chopmode.wordpress.com/2011/03/25/render_to_string-causes-subsequent-mail-rendering-to-fail/
https://rails.lighthouseapp.com/projects/8994/tickets/6623-render_to_string-in-mailer-causes-subsequent-render-to-fail
There are 2 ways you can make this work,
The first is to use the hack described in the first article above:
def email_invoice(invoice)
#invoice = invoice
attachments["invoice.pdf"] = WickedPdf.new.pdf_from_string(
render_to_string(:pdf => "invoice",:template => 'documents/show.pdf.erb')
)
self.instance_variable_set(:#lookup_context, nil)
mail :subject => "Your Invoice", :to => invoice.customer.email
end
Or, you can set the attachment in a block like so:
def email_invoice(invoice)
#invoice = invoice
mail(:subject => 'Your Invoice', :to => invoice.customer.email) do |format|
format.text
format.pdf do
attachments['invoice.pdf'] = WickedPdf.new.pdf_from_string(
render_to_string(:pdf => "invoice",:template => 'documents/show.pdf.erb')
)
end
end
end
I used of Unixmonkey's solutions above, but then when I upgraded to rails 3.1.rc4 setting the #lookup_context instance variable no longer worked. Perhaps there's another way to achieve the same clearing of the lookup context, but for now, setting the attachment in the mail block works fine like so:
def results_email(participant, program)
mail(:to => participant.email,
:subject => "my subject") do |format|
format.text
format.html
format.pdf do
attachments['trust_quotient_results.pdf'] = WickedPdf.new.pdf_from_string(
render_to_string :pdf => "results",
:template => '/test_sessions/results.pdf.erb',
:layout => 'pdf.html')
end
end
end
Heres' how I fixed this issue:
Removed wicked_pdf
Installed prawn (https://github.com/sandal/prawn/wiki/Using-Prawn-in-Rails)
While Prawn is/was a bit more cumbersome in laying out a document, it can easily sling around mail attachments...
Better to use PDFKit, for example:
class ReportMailer < ApplicationMailer
def report(users:, amounts:)
#users = users
#amounts = amounts
attachments["proveedores.pdf"] = PDFKit.new(
render_to_string(
pdf: 'bcra',
template: 'reports/users.html.haml',
layout: false,
locals: { users: #users }
)
).to_pdf
mail subject: "Report - #{Date.today}"
end
end