I have added the devise_invitable gem and a custom mailer. It works and normal devise emails (eg. confirmation_instructions) are reading the i18n file correctly, but invitation_instructions is not.
Here is the devise.en.yml:
en:
devise:
mailer:
confirmation_instructions:
subject: "Hi %{first_name}, please confirm your email to activate your account"
invitation_instructions:
subject: "%{first_name} has invited you to join their team!"
hello: "Oi"
someone_invited_you: "Pig"
The custom mailer class:
class CustomDeviseMailer < Devise::Mailer
def confirmation_instructions(record, token, opts={})
custom_options(record, opts, :confirmation_instructions)
super
end
def invitation_instructions(record, token, opts={})
custom_options(record, opts, :invitation_instructions)
super
end
private
def custom_options(record, opts, key)
opts[:subject] = I18n.t('subject', scope: [:devise, :mailer, key], first_name: record.first_name)
end
end
If I change the invitation_instructions method to this:
def invitation_instructions(record, token, opts={})
custom_options(record, opts, :invitation_instructions)
opts[:subject] = "TEST"
super
end
then the invitation email subject correctly changes to 'TEST'.
So why is it not reading the i18n file?
NOTE
I also generated the default invitation view and that is also not reading the i18n file. For example the first line of the default view is:
\app\views\devise\mailer\invitation_instructions.html.erb
<p>
<%= t("devise.mailer.invitation_instructions.hello", email: #resource.email) %>
</p>
Which should render 'Oi' from the i18n file, but it renders the default 'Hello' instead.
It turns out that the devise_invitable gem does not read devise.en.yml because it creates its OWN locale file:
devise_invitable.en.yml
and so that is where I need to make the text customisations.
Related
After a user signs up (Devise RegistrationController), I want to send them a welcome email.
Inside my User model, I created a function:
def send_welcome_email
UserMailer.welcome(self).deliver
end
Then I've added after_create :send_welcome_email
Inside the email view, I need to access variables.
<p><span><strong>Hi <%= self.name %>,</strong></span></p>
Returns an error:
undefined method `name' for #<#<Class:0x00007fa7d18e13b0>:0x00007fa7e4025358>
It makes sense that this would result in the error above, but I'm not sure how I can access variables from the model (that was just created).
I was following this asnwer: https://stackoverflow.com/a/17480095/9200273
Welcome method:
def welcome(user)
mail(
to: user.email,
subject: 'Welcome to Site!',
from: "support#site.com"
)
end
You can pass objects to the Mailer class using the with method like this:
UserMailer.with(user: self).welcome.deliver
Inside the UserMailer class:
def welcome_email
#user = params[:user]
...
end
In the view:
<%= #user.name %>
Reference: https://guides.rubyonrails.org/action_mailer_basics.html
I can't seem to find a step by step tutorial on how to integrate the Sendgrid web API in to a Ruby on Rails application. I'm pretty new to this so maybe I'm missing something obvious.
I would like to use the Sendgrid web API instead of the smtp delivery method (mailgun talks about the benefits of the web API over the SMTP method here: https://documentation.mailgun.com/quickstart-sending.html, and I was thinking that Sendgrid would either have the same benefits or I would potentially switch to mailgun later).
After installing the sendgrid gem (https://github.com/sendgrid/sendgrid-ruby), the documentation tells me to "Create a new client with your SendGrid API Key", and that I can do it 2 ways:
require 'sendgrid-ruby'
# As a hash
client = SendGrid::Client.new(api_key: 'YOUR_SENDGRID_APIKEY')
# Or as a block
client = SendGrid::Client.new do |c|
c.api_key = 'YOUR_SENDGRID_APIKEY'
end
Where specifically in my application am I supposed to put this code? Should I put this in my mailer, my application mailer or in the config/environments/production.rb file?
I took a look at this tutorial that walks through how to set up the Mailgun API: https://launchschool.com/blog/handling-emails-in-rails
According to this tutorial it looks like the line client = SendGrid::Client.new(api_key: 'YOUR_SENDGRID_APIKEY') should actually go in to the mailer method itself. See below for the launchschool.com example (presumably replacing the mailgun specific info with the sendgrid info):
class ExampleMailer < ActionMailer::Base
def sample_email(user)
#user = user
mg_client = Mailgun::Client.new ENV['api_key']
message_params = {:from => ENV['gmail_username'],
:to => #user.email,
:subject => 'Sample Mail using Mailgun API',
:text => 'This mail is sent using Mailgun API via mailgun-ruby'}
mg_client.send_message ENV['domain'], message_params
end
end
Additionally, how do I get my mailer method to send a mailer view instead of simple text as outlined in the launchschool example? For example, instead of sending the text 'This mail is sent using...' I would like to send a mailer view (something like account_activation.html.erb).
Finally, I am using Devise in my application, and I would like to have Devise use the web API to send emails (ie password reset, etc). Does this mean I need to create a custom mailer for Devise? If so, how do I do that?
According to Devise (https://github.com/plataformatec/devise/wiki/How-To:-Use-custom-mailer), I should "create a class that extends Devise::Mailer". Does that mean I simply make a file within my mailer folder with the info laid out in the docs? Do I need a separate mailer for Devise or can I have an existing mailer inherit from the Devise mailer? Finally, how do I tell devise to use the sendgrid web api to send emails (instead of the simple smtp method)?
Sorry for the long question, but hopefully others find it useful.
Thanks!
I would create a mailer class to do this
class SendgridWebMailer < ActionMailer::Base
include Sendgrid
def initialize
#client = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY']).client
end
def send_some_email(record, token)
mail = Mail.new
// do your mail setup here
mail = Mail.new
mail.from = Email.new(email: YOUR_EMAIL_HERE)
mail.subject = YOUR_SUBJECT
// I personally use sendgrid templates, but if you would like to use html -
content = Content.new(
type: 'text/html',
value: ApplicationController.render(
template: PATH_TO_TEMPLATE,
layout: nil,
assigns: IF_NEEDED || {}
)
mail.contents = content
personalization = Personalization.new
personalization.to = Email.new(email: EMAIL, name: NAME)
personalization.subject = SUBJECT
mail.personalizations = personalization
#client.mail._('send').post(request_body: mail.to_json)
end
end
Call it using
SendgridWebMailer.send_some_email(record, token).deliver_later
For Devise
class MyDeviseMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
def reset_password_instructions(record, token, opts={})
SendgridWebMailer.send_some_email(record, token).deliver_later
end
end
in config/devise.rb
# Configure the class responsible to send e-mails.
config.mailer = 'MyDeviseMailer'
EDIT:
This method will, unfortunately, only work with remote images.
Okay, so here's how I did it. There may be a better way to do this, but this worked for me.
So with sending an email you would originally send it by doing something like UserMailer.reset_email(user).deliver. So remove the deliver and save it to a variable:
object = UserMailer.reset_email(user)
Deeply nested in this ish is the body of the email. The place where it lies may change with context, so my advice is to return the object to the frontend so that you can dig into it and find it. For me, the body resided here:
object = UserMailer.reset_email(user)
body = object.body.parts.last.body.raw_source
Okay so now you got the raw source. Now to send it, here's a method I created:
def self.sendEmail(from,to,subject,message)
from = Email.new(email: from)
to = Email.new(email: to)
content = Content.new(type: 'text/html', value: message)
mail = Mail.new(from, subject, to, content)
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
response = sg.client.mail._('send').post(request_body: mail.to_json)
puts response.status_code
puts response.body
puts response.headers
end
Call it as such (in my case it's in the user model):
User.sendEmail('noreply#waydope.com',user.email,'Reset Password', body)
Make sure to change the content type from plain to html, or you will just get the raw code. Hope this helps.
Here's a step-by-step guide to help you integrate SendGrid into your RoR's app. Hope it helps!
Create a SendGrid account first at : http://sendgrid.com/
Create a new rails folder (sendgrid_confirmation)
rails new sendgrid_confirmation
Navigate to ‘sendgrid_confirmation’ folder
cd sendgrid_confirmation
Open ‘sendgrid_confirmation’ in your text editor (Sublime)
Create a user model (User is a test model, you can create any other model as per your project’s use)
rails generate scaffold user name email login
rake db:migrate
Include sendgrid-rails gem in your Gemfile
gem 'sendgrid-rails', '~> 2.0'
Run bundle install in your terminal
bundle install
Use secrets.yml to define the SendGrid API credentials: (config/secrets.yml)
production:
sendgrid_username: your-sendgrid-username
sendgrid_password: your-sendgrid-password
(Emails are not sent in development and test environments. So you can define it only for production.)
Generate a Mailer class. Mailer classes function as our controllers for email views.
rails generate mailer UserNotifier
Open app/mailers/user_notifier.rb and add the following mailer action that sends users a sign-up mail
class UserNotifier < ActionMailer::Base
default :from => 'any_from_address#example.com'
# send a signup email to the user, pass in the user object that contains the user's email address
def send_signup_email(user)
#user = user
mail(:to => #user.email,
:subject => 'Thanks for signing up for our amazing app')
end
end
Create a file app/views/User_notifier/send_signup_email.html.erb as follows: (This will create a view that corresponds to our action and outputs HTML for our email)
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<h1>Thanks for signing up, <%= #user.name %>!</h1>
<p>Thanks for joining and have a great day! Now sign in and do awesome things!</p>
</body>
</html>
Go to the Users Controller (app/controllers/users_controller.rb) and add a call to UserNotifier.send_signup_email when a user is saved.
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
UserNotifier.send_signup_email(#user).deliver
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
Update your config/environment.rb to point your ActionMailer settings to SendGrid’s servers. Your environment.rb file should look like the following:
# Load the Rails application.
require File.expand_path('../application', __FILE__)
# Initialize the Rails application.
Rails.application.initialize!
ActionMailer::Base.smtp_settings = {
:user_name => ‘your_sendgrid_username’,
:password => 'your_sendgrid_password’,
:domain => ‘your_domain.com',
:address => 'smtp.sendgrid.net',
:port => 587,
:authentication => :plain,
:enable_starttls_auto => true
}
Point your config/routes.rb file to load the index page on load. Add the following in your routes.rb file:
get ‘/’ => ‘users#index’
And that is it! Now when you create a new user, you should receive an email (on the user.email you provided) from any_from_address#example.com. You can change this from:e-mail from app/mailers/user_notifier.rb. Change the default :from address and it should do that. You can add email parameters in the send_signup_mail method inside the same file and add details that you’d like to add. You can also change the message body from app/views/user_notifier/send_signup_email.html.erb to display whatever content you want to.
I'm trying to make an app in Rails 4.
For the past 3 years, I've been struggling to figure out devise/omniauth (I am still trying to get it to work).
Stepping aside from the main problems while I try and find the will to live through this, I've tried to setup emails with Mandrill.
I found this tutorial, which I am trying to follow along: https://nvisium.com/blog/2014/10/08/mandrill-devise-and-mailchimp-templates/
I have a mailer called mandrill_devise_mailer.rb
class MandrillDeviseMailer < Devise::Mailer
def confirmation_instructions(record, token, opts={})
# code to be added here later
end
def reset_password_instructions(record, token, opts={})
options = {
:subject => "Reset your password",
:email => record.email,
:global_merge_vars => [
{
name: "password_reset_link",
# content: "http://www.example.com/users/password/edit?reset_password_token=#{token}"
content: "http://www.cr.com/users/password/edit?reset_password_token=#{token}"
},
{
name: "PASSWORD_RESET_REQUEST_FROM",
content: record.full_name
}
],
:template => "Forgot Password"
}
mandrill_send options
end
def unlock_instructions(record, token, opts={})
# code to be added here later
end
def mandrill_send(opts={})
message = {
:subject=> "#{opts[:subject]}",
:from_name=> "Reset Instructions",
# :from_email=>"example#somecorp.com",
:from_email=>["PROD_WELCOME"],
:to=>
[{"name"=>"#{opts[:full_name]}",
"email"=>"#{opts[:email]}",
"type"=>"to"}],
:global_merge_vars => opts[:global_merge_vars]
}
sending = MANDRILL.messages.send_template opts[:template], [], message
rescue Mandrill::Error => e
Rails.logger.debug("#{e.class}: #{e.message}")
raise
end
end
The differences between the above and what they have done in the tutorial are:
In my mail chimp mandrill template, I have:
Change my password
When I receive the email to reset the instructions, I get an underlined link to the change password form, which says 'change my password next to it. I want 'change my password to be the label which conceals the link text'.
Can anyone see what I've done wrong?
Here is how I created custom DeviseMailer
class MyDeviseMailer < Devise::Mailer
default template_path: 'devise/mailer' # to make sure that your mailer uses the devise views
def reset_password_instructions(record, token, opts={})
opts['from_email'] = "donotreply#mywebsite.com"
opts['from_name'] = "Password Reset"
#Rails.logger.mail.info "reset_password_instructions #{record.to_json} \n #{token.to_json} \n #{opts.to_json}"
super
end
end
https://github.com/plataformatec/devise/wiki/How-To:-Use-custom-mailer and
Add dynamic value in devise email subject
I am using the Mandrill API to handle my transactional email for a variety of reasons. One issue I am encountering is generating the confirmation_url, edit_password_url and unlock_url in the new mailer. I am including Devise's URL helpers in the new mailer. Everything else in the email is being generated properly.
I am getting the following error:
NoMethodError (undefined method `main_app' for #<DeviseMailer:0x007f812b6abe78>):
app/mailers/devise_mailer.rb:15:in `confirmation_instructions'
app/controllers/lenders/registrations_controller.rb:9:in `create'
devise_mailer.rb
class DeviseMailer < MandrillMailer::TemplateMailer
helper :application
include Devise::Controllers::UrlHelpers
default from: 'no-reply#test.com'
def confirmation_instructions(record, token)
mandrill_mail template: 'Confirmation Instructions',
subject: 'Confirm Email',
from_name: 'Test',
to: { email: record.email },
vars: {
'FNAME' => record.first_name,
'LIST_COMPANY' => "Apples",
'HTML_LIST_ADDRESS_HTML' => "1 Infinite Loop",
'CONFIRMATION_LINK' => confirmation_url(record, :confirmation_token => token)
}
end
end
Thank you for the help
For anyone hitting this issue - I had the same method missing issue when using mandrill_mailer to replace devise's customer mailer.
Using the above example above I fixed it like so
class DeviseMailer < MandrillMailer::TemplateMaile
# ..... mailer code in here here .....
private
def main_app
Rails.application.routes.default_url_options[:host] = Rails.application.routes.default_url_options[:host]
Rails.application.routes.url_helpers
end
end
Ok I have seen many discussions about customizing devise email subject but none seems to solve what I want. Currently my confirmation email subject reads "Confirm your Qitch.com account". I want to customize this email subject and add a dynamic value of a user's name in it such that if user ALEX signs up for an account, he should get an email address with the subject, Welcome ALEX, confirm your Qitch.com account. How can I achieve this in devise?
devise.en.yml
mailer:
confirmation_instructions:
subject: 'Confirm your Qitch.com account'
reset_password_instructions:
subject: 'Reset your Qitch.com password'
unlock_instructions:
subject: 'Unlock your Qitch.com account'
Lastly, how do I add a name in the reply address or from address, currently when you receive the mail, it says sender: no-reply#qitch.com Is there a way I can customize it to Qitch
Thanks
I see that no answers are clean enough so I would like to make a short summary here.
First of all, you have to tell Devise you're going to override its origin mailer methods by:
config/initializers/devise.rb
config.mailer = 'MyOverriddenMailer'
After that, you need to create your overridden mailer class and override whatever method you want like this:
app/mailers/my_overridden_mailer.rb
class MyOverriddenMailer < 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={})
if record.name.present?
opts[:subject] = "Welcome #{record.name}, confirm your Qitch.com account"
else
opts[:subject] = "Confirm your Qitch.com account"
end
super
end
end
Remember to restart your Rails server to apply the changes! :)
Note:
List of opts options: subject, to, from, reply_to, template_path, template_name.
record is the instance of User model
And of course, origin document
Devise helper here and How To: Use custom mailer
class MyMailer < Devise::Mailer
def confirmation_instructions(record, opts={})
headers = {
:subject => "Welcome #{resource.name}, confirm your Qitch.com account"
}
super
end
def reset_password_instructions(record, opts={})
headers = {
:subject => "Welcome #{resource.name}, reset your Qitch.com password"
}
super
end
def unlock_instructions(record, opts={})
headers = {
:subject => "Welcome #{resource.name}, unlock your Qitch.com account"
}
super
end
end
Or
class MyMailer < Devise::Mailer
...
...
private
def headers_for(action)
if action == :confirmation_instructions
headers = {
:subject => "Welcome #{resource.name}, confirm your Qitch.com account"
}
elsif action == :reset_password_instructions
headers = {
:subject => "Welcome #{resource.name}, reset your Qitch.com password"
}
else
headers = {
:subject => "Welcome #{resource.name}, unlock your Qitch.com account"
}
end
end
end
And tell devise to use your mailer:
#config/initializers/devise.rb
config.mailer = "MyMailer"
NOTE : I haven't tried them yet, but they may be helpful and for anyone, please correction my answer, if there is an error you could edit my answer
I'm working with devise (3.2.1) and I've implemented the following solution, but to modify the from: field with localization:
# app/mailers/devise_mailer.rb
class DeviseMailer < Devise::Mailer
def confirmation_instructions(record, token, opts={})
custom_options(opts)
super
end
def reset_password_instructions(record, token, opts={})
custom_options(opts)
super
end
def unlock_instructions(record, token, opts={})
custom_options(opts)
super
end
private
def custom_options(opts)
opts[:from] = I18n.t('devise.mailer.from', name: Tenancy.current_tenancy.name, mail: ENV['FROM_MAILER'] )
end
end
Then I've defined the message in my locale files
# config/locales/devise.es.yml
es:
devise:
mailer:
from: "Portal de trabajo %{name} <%{mail}>"
To modify the subject, it should be almost the same:
def confirmation_instructions(record, token, opts={})
custom_options(opts, :confirmation_instructions)
super
end
private
def custom_options(opts, key)
opts[:from] = I18n.t('subject', scope: [:devise, :mailer, key])
end
# and in your locale file
es:
devise:
mailer:
confirmation_instructions:
subject: Instrucciones de confirmación
Hook in headers_for to e.g. prefix the subject for all devise mails.
# config/initializers/devise.rb
Devise.setup do |config|
# ...
config.mailer = 'DeviseMailer'
# ...
end
# app/mailers/devise_mailer.rb
class DeviseMailer < Devise::Mailer
def headers_for(action, opts)
super.merge!({ subject: 'Hi ALEX! ' + subject_for(action) })
end
end
Yo should create an ActionMailer like this one:
class Sender < ActionMailer::Base
default :from => "'Eventer' <dfghjk#gmail.com>"
def send_recover(user, pw)
mail(:to => user.email , :subject => "You have requested to change your password from Eventer") do |format|
format.text {
render :text =>"Dear #{user.name},
**Body**
Regards."
}
end
end
end
Then you should call it from the controller this way:
Sender.send_recover(#mail, current_user, #meetup).deliver
I hope it works for you!
Regards