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 :)
Related
Ruby on Rails Project, I'm running Resque to process emails (in particular Devise and Devise Invitable). Devise emails are sending fine (out via API through postmark gem) but devise_invitable emails are failing in Resque with undefined method invitation_instructions for #User:0x0xxxxxx Did you mean? invitations_count.
When sending through the default devise mailer it all works fine, but fails with my custom mailer. I am wanting to add an inline attachment, hence the need for a custom mailer.
Custom Devise Mailer:
class DeviseMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
include Devise::Mailers::Helpers
include DeviseInvitable::Mailer
default template_path: 'devise/mailer'
default from: sender#example.com
layout 'mailer_devise'
before_action :add_inline_attachment!
def reset_password_instructions(record, token, opts={})
super
end
def invitation_instructions(record, token, opts={})
#token = token
devise_mail(record)#, record.invitation_instructions || :invitation_instructions, opts)
end
private
def add_inline_attachment!
attachments.inline['logo.png'] = File.read(Rails.root.join('app/assets/images/logo.png'))
end
end
Summary:
def reset_password_instructions - working fine
def invitation_instructions - failing with undefined method error
I ended up migrating to Sidekiq for other reasons. Below is my updated Devise Mailer Class, which is working well. There was no need to define the invitation_instructions method as the before_action callback was attaching the inline image, what was what i was after in the beginning.
class DeviseMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
include Devise::Mailers::Helpers
include DeviseInvitable::Mailer
default template_path: 'devise/mailer'
default from: ENV['EMAIL_NAME']
layout 'mailer_devise'
before_action :add_inline_attachment!
private
def add_inline_attachment!
attachments.inline['logo.png'] = File.read(Rails.root.join('app/assets/images/logo.png'))
end
end
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 am using custom mailer by overriding the devise mailer. It is working fine. But I need to pass some data to the mailer template so that when the confirmation email send to the user it contains some dynamic content. I have tried it using sessions,#resource and current_user method but both are not working. Is there any way to do that?
Custom mailer
class CustomMailer < 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 you mailer uses the devise views
def confirmation_instructions(record, token, opts={})
opts[:subject] = "Email Confirmation"
opts[:from] = 'no-reply#abc.com'
#data = opts[:custom_field]
super
end
end
in the controller
CustomMailer.confirmation_instructions(token, {custom_field: "abc"})
This is the code in template
We are happy to invite you as user of the <b> <%= #data %> </b>
Thanks.
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 invite(sender, recipient)
#sender = sender
#recipient = recipient
mail( :to => recipient.email,
:subject => "Invite by #{sender.name}"
)
end
...
end
You can now call the invite in your project and pass whatever variable you want to be able to access in your template.
i.e:
Calling the invite method:
DeviseMailer.invite(current_user, newContact).deliver
So in your view you can then just call the variable:
invite.html.erb
<p>Hello <%= #recipient.email %></p>
<% if #sender.email? %>
<p> some additional welcome text here from <%= #sender.email %> </p>
<% end %>
EDIT
To answer your specific question here is what you want to override:
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
Then call it anywhere you want:
DeviseMailer.confirmation_instructions(User.first, "faketoken", {})
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