I want to specify a custom 'from' email when sending out devise emails.
So far I have created a customer mailer, and am overwriting part of the default methods, but I have no clue on how to pass in arguments, like current_user
Example:
class CustomDeviseMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
default template_path: 'devise/mailer'
def confirmation_instructions(record, token, opts = {})
Rails.logger.debug 'Sending custom confirmation instructions...'
customer_responsible = current_user.company
info_email = customer_responsible.info_email
opts[:from] = info_email if info_email
super
rescue => e
Rails.logger.debug e.inspect
throw e
end
end
Related
I use Devise for authentication in my Rails app.
In my registrations_controller I have a variable like this:
class RegistrationsController < Devise::RegistrationsController
def create
foo = "bar"
super
end
end
In my customized mailer I then try to access the foo variable. The opts argument seems to be the one to look at:
class CustomMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
def confirmation_instructions(record, token, opts={})
Rails.logger.error opts[:foo].inspect
super
end
end
But how do I pass on the foo variable on, without overwriting a lot of methods?
First, read about Devise custom mailer to familiarize yourself with the process.
Briefly this is how you'd go about doing it:
in config/initializers/devise.rb:
config.mailer = "DeviseMailer"
Now you can just use DeviseMailer like you'd do for any other mailer in your project:
class DeviseMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
default template_path: 'devise/mailer' # to make sure that your mailer uses the devise views
...
def confirmation_instructions(record, token, opts={})
headers["Custom-header"] = "Bar"
opts[:from] = 'my_custom_from#domain.com'
opts[:reply_to] = 'my_custom_from#domain.com'
super
end
...
end
You can now call the confirmation_instructions in your project and pass whatever variable you want to be able to access in your template.
i.e:
Calling the confirmation_instructions method:
DeviseMailer.confirmation_instructions(User.first, "faketoken", {})
confirmation_instructions.html.erb
<p> And then override the template according to you. <p>
Hope this will help you :)
I'd like to pass session data from my Registrations controller to my custom mailer, which is triggered in the create action. I'm able to do this using a global variable, but would greatly prefer not to go that route. The data I'd like to pass is stored in the welcome_email_token variable.
registrations_controller.rb:
module Users
class RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
def create
client_with_valid_email_token = Client.registerable_email_token(session[:email_token])
if client_with_valid_email_token.first.present?
welcome_email_token = client_with_valid_email_token.first.email_token
super
end
end
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
end
end
end
custom_mailer.rb:
class CustomMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
default template_path: 'devise/mailer'
def confirmation_instructions(record, token, opts = {})
client = Client.find_by(email_token: welcome_email_token)
#first_name = client.first_name
super
end
end
The code as written returns nil for user, leading user.first_name to throw an error. Is there a way I can pass this information to the mailer without using a global variable?
I guess the record you are looking for, is already passed as the first parameter of this method. If you are changing so little to the method, why just go with the standard method already provided by Devise?
class CustomMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
default template_path: 'devise/mailer'
def confirmation_instructions(record, token, opts = {})
#user = record
super
end
end
The super is basically also calling the original confirmation_instructions method from Devise, which is:
def confirmation_instructions(record, token, opts={})
#token = token
devise_mail(record, :confirmation_instructions, opts)
end
That uses a helper method called devise email:
# Configure default email options
def devise_mail(record, action, opts = {}, &block)
initialize_from_record(record)
mail headers_for(action, opts), &block
end
So I don't really see what you are trying to achieve in your method, that's not already handled by Devise.
I have my scope views reset_password_instructions.html
and in my devise.rb
config.scoped_views = true
It works fine in development and sends the custom email. However, in production, when user receives email, device sent a default template.
How can I fix this in production?
when I use devise I this is what I do to send my own emails
class MyMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
default template_path: 'devise/mailer' # to make sure that your mailer uses the devise views
end
now, in your config/initializers/devise.rb, you can set config.mailer to "MyMailer".
You may now use your MyMailer in the same way as any other mailer. In case you want to override specific mails to add extra headers, you can do so by simply overriding the method and calling super at the end of your custom method, to trigger Devise's default behavior.
You can also override any of the basic headers (from, reply_to, etc) by manually setting the options hash:
def confirmation_instructions(record, token, opts={})
headers["Custom-header"] = "Bar"
opts[:from] = 'my_custom_from#example.com'
opts[:reply_to] = 'my_custom_from#example.com'
super
end
In order to get preview (if User is your devise model name):
# test/mailers/previews/my_mailer_preview.rb
# Preview all emails at http://localhost:3000/rails/mailers/my_mailer
class MyMailerPreview < ActionMailer::Preview
def confirmation_instructions
MyMailer.confirmation_instructions(User.first, "faketoken", {})
end
def reset_password_instructions
MyMailer.reset_password_instructions(User.first, "faketoken", {})
end
def unlock_instructions
MyMailer.unlock_instructions(User.first, "faketoken", {})
end
end
I hope that this was useful :)
Given that request object is not working in ActionMailer, is there a way to detect current URL and set from option for custom devise mailer controller?
So this is what I have so far in application_controller.rb:
before_filter :images, :hide_sidebar, :global_vars, :set_mailer_host
before_action :sidebar_menu
def set_mailer_host
ActionMailer::Base.default_url_options[:host] = request.host_with_port
#host = request.env['HTTP_HOST'] unless request.env['HTTP_HOST'].include? 'localhost'
end
Devise Mailer custom class:
class DeviseMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
def confirmation_instructions(record, token, opts={})
if record.events.present?
name = record.events.last.name
#event = name
else
name = "#{record.first_name},"
end
opts[:from] = "noreply##{#host}" if #host
opts[:subject] ="#{name} Registration - Confirmation Required"
super
end
end
Also in Devise.rb I've set below:
config.mailer = 'DeviseMailer'
I don't know why I'm still getting default domain instead of the current URL.
Since I have multiple urls pointing to the same application, is there a way to detect this and configure for Devise Mailers?
Why won't you pass the host as argument to your mailer?
def confirmation_instructions(record, token, opts={}, host)
...
end
DeviseMailer.confirmation_instructions(..., #host).deliver
on default, the subject for invitation mail is
mailer:
invitation_instructions:
subject: 'Invitation instructions'
I'd like to change it to
subject: '%{invited_by} has invited you!'
but this requires to have invited_by variable accessible to the translate method for i18n.
How can I have this variable accessible/declared without changing default behavior too much?
Devise default mailer won't work for you, but it's quite easy to set up a custom mailer and define there your own subject_for helper:
# in config/initializers/devise.rb:
...
config.mailer = "CustomDeviseMailer"
...
# in app/mailers/custom_devise_mailer.rb:
class CustomDeviseMailer < Devise::Mailer
protected
def subject_for(key)
return super unless key.to_s == 'invitation_instructions'
I18n.t('devise.mailer.invitation_instructions.subject',
:invited_by => resource.invited_by.try(:full_name) || 'Someone')
end
end