Devise doesn't override email views - ruby-on-rails

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 :)

Related

Override Devise Invitable `invite!` method in order to add attachment to invitation email - Rails

I have devise_invitable gem and an invitations controller InvitationsController < Devise::InvitationsController. I invite my user with this method User.invite!(user, inviter) from the gem.
Now, I would like to add pdf attachment to this invitation but I am not able to override the mailer method in order to do stuff like this: attachments['terms.pdf'] = File.read('/path/terms.pdf').
My invitation email is working fine and I would like to keep it like that but with an attachment.
Here my routes.rb:
devise_for :users, controllers: { invitations: 'api/v1/users/invitations' }
What am I missing?
Thanks 🙏
DeviseInvitable has a usefull skip_invitation option.
With the following method you can easily use your own mailer and add attachment in it :
def invite_and_notify_user(email)
user = User.invite!({ email: email }) do |u|
u.skip_invitation = true
end
notify_by_email(user)
end
def notify_by_email(user)
MyUserMailer.invited_user_email(user).deliver
end
In MyUserMailer class:
def invited_user_email(user)
#user = user
attachments['terms.pdf'] = File.read('/path/terms.pdf')
mail(to: user.email, from: 'contact#mail.com', subject: "Your subject")
end
You can override the devise mailer like so...
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
# If there is an object in your application that returns a contact email, you can use it as follows
# Note that Devise passes a Devise::Mailer object to your proc, hence the parameter throwaway (*).
default from: ->(*) { Class.instance.email_address }
end
You can then in your config/initializers/devise.rb, set config.mailer to "MyMailer". Note that this overrides the mailer used for all devise modules.
Then in that class add your attachment to invited_user_instructions...
def invited_user_instructions(*args)
attachments['terms.pdf'] = File.read('/path/terms/pdf')
super
end
This is devise's instructions https://github.com/heartcombo/devise/wiki/How-To:-Use-custom-mailer

Pass variable from Devise controller to custom mailer?

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 :)

Failed Resque Job for Devise Invitable - undefined method `invitation_instructions' for #<User:0x

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

How to pass data to custom mailer in rails?

I am using custom mailer to send confirmation email to user but I need to pass some data to the mailer. I have tried using session and application helper but no success.
Application helper is not able to access session data.
mailer is also not able to access session data.
This is my code
class CustomMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
#include ApplicationHelper
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#test.com'
end
end
I dont want to call custommailer from controller. I just want to pass data.
Is this possible?

Devise mailer custom domain from option

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

Resources