I want to send devise confirmation mails using sendgrid templates, so I have a template on sendgrid which I will use and this code in rails
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 your mailer uses the devise views
def confirmation_instructions(record, token, opts={})
headers["X-SMTPAPI"] => {
"sub": {
"%record%" => [record],
"%token%" => [token],
},
"filters": {
"templates": {
"settings": {
"enable": 1,
"template_id": 'id here'
}
}
}
}.to_json
mail(
subject: "subject"
)
end
the problem is I have this error when it tries to send the mail:
An ActionView::Template::Error occurred in registrations#create:
undefined method `url' for nil:NilClass
app/views/devise/mailer/confirmation_instructions.html.erb:5:in `_app_views_devise_mailer_confirmation_instructions_html_erb___3785479096466683301_70149801011060'
so it seems like rails trying to send email the default way and no sengrid flow was triggered
Related
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.
I'm trying to figure out how to setup my Rails 4 app so that the devise mailer sends through Postmark, using Postmark Templates.
I have postmark-rails gem in my gem file.
I have everything working, except I can't figure out how to give the confirmation token to postmark.
In Postmark I have this template:
To get started, please confirm your account below:
action_url_Value
I can't figure out how to put the following line into the template instead of action url value:
<%= link_to 'Confirm my account', confirmation_url(#resource, confirmation_token: #token) %></div>
I have added the following to my initializer/devise.rb:
config.mailer = 'PostmarkDeviseMailer'
In postmark_devise_mailer.rb, I have:
class PostmarkDeviseMailer < ActionMailer::Base
include Devise::Mailers::Helpers
def message
mail(
:subject => 'Hello from Postmark',
:to => 'sender#address.com',
:from => 'sender#address.comm',
:html_body => '<strong>Hello</strong> dear Postmark user.',
:track_opens => 'true')
end
end
default from: "sender#address.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
The next steps are less clear to me. Has anyone figured out how to use Postmark Templates as the mailer for devise transaction emails?
The first thing you need to do is create a custom mailer in app/mailers in this example it is user_mailer.rb If you are reading this answer, I am assuming you both have a postmark account if not grab one here: Postmark Registration and I am assuming you have a template and layout selected.
One important item to remember is to make sure you assign an alias to your template. This can be found in a small link found above the template title in small gray text.
The items within self.template_model are 100% customizable and are shown in your layout / template editors as so: {{product_name}}.
class UserMailer < Devise::Mailer
include PostmarkRails::TemplatedMailerMixin
helper :application
include Rails.application.routes.url_helpers
include Devise::Controllers::UrlHelpers
default from: 'from_you#something.com'
default reply_to: 'support#hammer-lane-industries.com' # Optional
def confirmation_instructions(record, token, opts={})
#admin = record
#token = token
# this will pass the data via the postmark-api to your template / layout these fields are 100% customizable and can be cahnged in your template / layout
self.template_model = { product_name: 'your product',
username: #user.username,
email: #user.email,
confirmation_url: user_confirmation_url(#resource, confirmation_token: #token, subdomain: 'add subdomain here if needed'),
login_url: login_root_url(subdomain: 'add subdomain here if needed'),
trial_length: '14-days',
sender_name: 'What ever you want',
action_url: user_confirmation_url(#resource, confirmation_token: #token, subdomain: 'add subdomain here if needed'),
parent_company_name: 'Your company name',
company_address: 'Your address'}
mail to: #admin.email, postmark_template_alias: 'devise_user_confirmation'
end
end
Rinse and repeat with unlocks and password mailers, all you will need to know is the urls that you'll be sending the tokens to.
To finish it all off:
You will need to add a small block to your devise resource model, in this example I've used user.rb
class User < ApplicationRecord
devise :database_authenticatable, :confirmable,
:recoverable, :rememberable, :validatable,
:timeoutable, :trackable
def devise_mailer
UserMailer # Must match your mailer class
end
end
Once this is done, restart your rails server, and give it a go!
I hope this helps you with hooking Postmark Templates (API) into Devise mailers
Response from Postmark help desk is:
Unfortunately, Postmark templates don’t fit nicely into the traditional Rails ecosystem. See my reply here for more details:
https://github.com/wildbit/postmark-rails/issues/53
To sum it up, by using the Postmark API with Postmark templates you could potentially replace ActionMailer, but mixing them together presents a significant challenge. Unfortunately, we’ve never tried the "ActionMailer-free" approach with Devise, so I can’t help you here. Unless you have a good reason not to, I’d suggest going with ActionMailer templates and using the postmark-rails gem as a delivery adapter. This approach is known to work well with Devise. Please let me know if you have any questions.
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