I am trying to build a password reset email. I am Using Rails 3.2.16, and Ruby 1.9.3p327, I have read a ton of answers to this question and tried pretty much everything. I have also gone through the action mailer basics guide, and as far as i can see this should be working but its just not going well. Ill do a step by step guide of how i set it up.
firstly since i am trying to get this working in development, within development.rb Note: I have reset the application each time i edited the development.rb file.
#this is all the action mailer settings i have defined in development.rb
config.action_mailer.raise_delivery_errors = true # Set to true so that errors would be visible.
config.action_mailer.perform_deliveries = true # I read about this possible fix on SO
config.action_mailer.default_url_options = {
host: "boogle.dev"
}
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:address => "smtp.office365.com",
:port => 587,
:domain => "mpowered.co.za",
:user_name => "support#mpowered.co.za",
:password => "password",
:authentication => :login,
:enable_starttls_auto => true
}
My notifier class which inherits from ActionMailer
class Notifier < ActionMailer::Base
default from: "Mpowered - BEEtoolkit <support#mpowered.co.za>"
def deliver_password_reset_email(user)
#edit_password_reset_url = edit_password_reset_url(user.perishable_token)
#name = user.name
mail(
subject: "Password Reset Instructions",
to: user.email,
date: Time.now,
content_type: "text/html")
end
end
Within my User model i have set up the method which will send the mail along with setting up of a perishable_token
def deliver_password_reset_instructions!
reset_perishable_token!
Notifier.deliver_password_reset_email(self)
end
The Passwords reset controller is set up like this:
class PasswordResetsController < ApplicationController
before_filter :require_no_user
before_filter :load_user_using_perishable_token, :only => [ :edit, :update ]
def new
end
def create
#user = User.find_by_email(params[:email])
if #user
#user.deliver_password_reset_instructions!
flash[:notice] = "Instructions to reset your password have been emailed to you"
render action: :new
else
flash.now[:error] = "No user was found with email address #{params[:email]}"
render action: :new
end
end
def edit
end
def update
#user.password = params[:password]
# Only if your are using password confirmation
#user.password_confirmation = params[:password]
# Use #user.save_without_session_maintenance instead if you
# don't want the user to be signed in automatically.
if #user.save
flash[:success] = "Your password was successfully updated"
redirect_to #user
else
render action: :edit
end
end
private
def load_user_using_perishable_token
#user = User.find_using_perishable_token(params[:id])
unless #user
flash[:error] = "We're sorry, but we could not locate your account"
redirect_to root_url
end
end
end
I added resources to my routes:
resources :password_resets, :only => [ :new, :create, :edit, :update ]
My views are simple:
in app/views/password_resets/new.haml.html
%br
= form_tag password_resets_path, class: 'form-inline' do
%legend Forgotten Password
%p Enter your email address and instructions to reset your password will be emailed to you:
%span.span1
= label_tag :email, 'Email'
= text_field_tag :email
= submit_tag 'Reset my password', class: 'btn'
%br
So this should send the mail once you submit a valid email.
You should then receive an email with this content: app/views/notifier/password_reset_instructions.html.haml
%h1 Password Reset Instructions
%p
A request to reset your password has been made. If you did not make
this request, simply ignore this email. If you did make this
request, please follow the link below.
= link_to "Reset Password!", #edit_password_reset_url
The link should bring you to a form where you can then save a new password and password confirmation.
app/views/password_resets/edit.html.haml
- if #user
= form_for #user, :url => password_reset_path, :method => :put do |f|
%legend Change My Password
%p Please select a new password for your account
.span8
= f.field :password, :field_type => :password_field, :label => "New password"
= f.field :password_confirmation, :field_type => :password_field
.clearfix
= f.submit "Update my password", class: 'btn'
- else
%h3 We couldn't identify your reset code
%p We're sorry, but we could not locate any accounts with reset codes that matched yours.
%p If you are having difficulties resetting your password, try copying and pasting the URL from your password reset email into your browser or restarting the password reset process.
to which you can save your new password and then login once more.. this is what i have set up in the app. but everytime i try send it by following the system, it says the email was sent but nothing ever comes. I have also tried loading up a user in the console and then running u.deliver_password_reset_instructions!
and i get this:
But still no email in my inbox. I have currently set the email address in the notifier to my own personal one so no matter what valid email address is requested, the email should come to me.
I have been hitting walls for the last 12 hours and have no idea where to turn. i am hoping i have made a balls up that a fresh pair of eyes can catch.
You need to add .deliver when calling Mailer method like this
def deliver_password_reset_instructions!
reset_perishable_token!
Notifier.deliver_password_reset_email(self).deliver
end
Hope this helps
Related
I'm using Rails 6 and minitest with the built-in system tests (which use Capybara I think) and using FactoryBot as well to generate my test records.
I have a pretty standard password rest feature I'm trying to implement.
I've verified that when I go to the pages in the browser and fill out the information it does indeed change the user's password, but for some reason the password is never changed in the test.
It's almost like the #user object is being cached in the tests and won't reload in the test, but I have no idea why that would be.
Anyone know why this test would fail but the functionality works in "real life" when I manually change a password?
# test/system/password_resets_test.rb
require "application_system_test_case"
class PasswordResetsTest < ApplicationSystemTestCase
test "change password" do
original_password = "password"
new_password = "new-password"
#user = create(:user, password: original_password, password_reset_token_sent_at: Time.current)
visit password_reset_path(#user.password_reset_token)
fill_in "user[password]", with: new_password
click_on "Update Password"
assert_equal(#user.reload.password, new_password)
end
end
# app/views/password_resets/show.html.erb
<%= form_with model: #user, url: password_reset_path(#user.password_reset_token), method: :put do |form| %>
<div class="field">
<%= form.label :password, "Password" %><br />
<%= form.password_field :password, autofocus: true, required: true %>
</div>
<div class="field">
<%= form.submit "Update Password" %>
</div>
<% end %>
# app/controllers/password_resets_controller.rb
class PasswordResetsController < ApplicationController
def show
if #user = User.find_by(password_reset_token: params[:id])
if #user.password_reset_token_expired?
flash[:error] = "Your password reset has expired"
redirect_to new_password_reset_path
end
else
flash[:error] = "Invalid password reset token"
redirect_to new_password_reset_path
end
end
def update
#user = User.find_by(password_reset_token: params[:id])
new_password = password_reset_params[:password]
# Automatically set `#password_confirmation` so user does not have
# to enter in password twice on reset page.
if #user&.update(password: new_password, password_confirmation: new_password)
let_user_in(#user)
else
render :show
end
end
private
def password_reset_params
params.require(:user).permit(:password)
end
# app/models/user.rb
class User < ApplicationRecord
PASSWORD_RESET_TIME_LIMIT_IN_HOURS = 4.freeze
has_secure_password
has_secure_token :password_reset_token
validates :password,
presence: true,
length: { minimum: 8 },
allow_nil: true
def password_reset_token_expired?
return true if password_reset_token_sent_at.blank?
password_reset_token_sent_at < PASSWORD_RESET_TIME_LIMIT_IN_HOURS.hours.ago
end
end
click_on doesn't guarantee any actions triggered by the click have happened when it returns. This is because Capybara has no way of knowing what (if any) actions would have been triggered by that click. This means your assertion of the new password is probably happening before the page has even submitted. To fix that you need to use one of the Capybara provided retrying assertions (which assert_equal is not) to check for something visible on the page that indicates the update has occurred.
Something along the lines of
click_on "Update Password"
assert_text "Password Updated!" # whatever message your page shows to indicate successful password update
assert_equal(#user.reload.password, new_password)
should fix your issue.
I am sending email using action mailer in my rails app. But it allows only one default sender. This is my UserMailer class:
class UserMailer < ActionMailer::Base
default :from => "example#example.com"
def welcome_email(user, order)
#user = user
#order = order
mail(:to => user.email, :subject => "Your Order")
end
def signup_email(user)
#user = user
mail(:to => user.email, :subject => "Thank you.")
end
def invite_confirm(curuser,usemail,post)
#greeting = "Hi"
#user = curuser
#post = post
mail(:to => user.email, :subject => "Hello")
end
end
I tried this:
class UserMailer < ActionMailer::Base
def welcome_email(user, order)
#user = user
#order = order
mail(:to => user.email, :subject => "Your Order", :from => "abc#xyz.com")
end
def signup_email(user)
#user = user
mail(:to => user.email, :subject => "Thank you.", :from => "qwe#asd.com")
end
def invite_confirm(curuser,usemail,post)
#greeting = "Hi"
#user = curuser
#post = post
mail(:to => user.email, :subject => "Hello", :from => "zyx#asd.com")
end
end
But still it is sending email from "example#example.com"
Is there any way to change sender for each method written in UserMailer class? Am i supposed to change anywhere else?
In config/environments/development.rb and config/environments/production.rb i have this:
config.action_mailer.smtp_settings = {
:address => "smtp.gmail.com",
:port => "587",
:domain => "gmail.com",
:authentication => "plain",
:user_name => "example#example.com",
:password => "example",
:enable_starttls_auto => true
}
I guess, i should not change anything here.
You can pass it as a parameter to the mail method:
def new_mail
mail from: "example#example.com", to: "user#example.com"
end
I think you want to send mail with three different emails of the for-each action. Because you use gmail, you need Sending mail from a different address.
No single vendor is optimal for all three types of email; you likely
will use several vendors.
For “company email,” that is, sending individual email to customers or
business associates, you’ll probably use Gmail or Google Apps for
Business. For a single address, you can set up a single Gmail account
to receive and send email from a different address. More likely,
you’ll want several email addresses for your company mail. For that,
use Google Apps for Business.
Send Email with Rails
I found that, this can't be done using smtp. Need to use amazon SES which allows multi sender support.
Here's what i use, it allows to make a "title" different.
class UserMailer < ActionMailer::Base
default :from => '"example" <example#domain.com>'
def send_signup_email(user)
#user = user
mail(to: #user.email, subject: 'example')
end
end
I'm struggling with Heroku sending gmail with actionmailer in my Rails app.
I've got my gmail username and password correctly set as Heroku config vars, but I still get authentication errors. I finally discovered that I need to create an initializer but I'm having trouble hooking everything together. The following code brings up the correct username and password in the heroku logs, but with a NoMethodError. Not sure where to put the method to make it all work. I've tried putting it in my users_controller but that makes the whole thing crash.
I'm planning to add my S3 stuff to this initializer once I get this working.
my initializers/heroku.rb
GMAIL_CREDENTIALS = { :username => ENV['GMAIL_USERNAME'],
:password => ENV['GMAIL_PASSWORD'] }
my mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
default from: "mygmailaddress", :gmail_credentials => GMAIL_CREDENTIALS
def signup_confirmation(user)
#user = user
mail to: user.email, subject: "Sign Up Confirmation"
end
end
and create in my users_controller:
def create
#user = User.new(params[:user])
if #user.save
UserMailer.signup_confirmation(#user).deliver
sign_in #user
flash[:success] = "Welcome to Plain Vanilla!"
redirect_to #user
else
render 'new'
end
end
The error message in heroku logs:
Completed 500 Internal Server Error in 482ms
app/controllers/users_controller.rb:21:in `create'
NoMethodError (undefined method `encoding' for {:username=>"mygmailusername",
:password=>"mypassword"}:Hash):
Thanks for any help!
Charlie
I normally put my gmail credentials in an intializer called mailer.rb. It looks like this:
ActionMailer::Base.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => "mail.google.com",
:user_name => ENV['MY_GMAIL_USER_NAME'],
:password => ENV['MY_GMAIL_PASSWORD'],
:authentication => "plain",
:enable_starttls_auto => true
}
Where did you get the code you're using? I'd try dropping the :gmail_credentials line from your user_mailer and using an initializer that's more like mine. Don't forget to restart your app if you change these files.
Hope this helps,
I'm trying to use action mailer to send a simple email to a user whenever they sign up, but action mailer doesn't seem to be sending any emails at all. The funny thing is that the redirect code works and the page renders properly on the local host, but on heroku it says "Sorry something went wrong".
Here's what I'm doing in my users controller:
if #user.save
UserMailer.welcome_email(#user).deliver
flash[:success] = "Congratulations, you've created your band app account!"
redirect_to root_path
else
render 'new'
end
The welcome_email method referred to in the controller:
class UserMailer < ActionMailer::Base
default from: "admin#something.com"
def welcome_email(user)
#user = user
#url = signin_path
mail(to: user.email, subject: "Your band app account has been created" )
end
end
and finally the code for the email view (welcome_email.html.erb in app/views/user_mailer:
<!DOCTYPE html>
<html>
<body>
<h1>Welcome to the band app, <%= #user.name %></h1>
<p>
Your band app account has been created.<br/>
</p>
<p>
To login to the site, just follow this link: <%= #url %>.
</p>
<p>Thanks for joining and have a great day!</p>
</body>
</html>
Thank you in advance for any help!
Did you set up your environment file?
As it says here http://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration-for-gmail, you should set your development.rb or the file related to the ENV you are working in with the following settings:
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => 'baci.lindsaar.net',
:user_name => '<username>',
:password => '<password>',
:authentication => 'plain',
:enable_starttls_auto => true }
I know that the link says it's an example for Gmail, but you have to set up those configurations on your ENV file, like the email you are using(:user_name), the password, etc.
I'm trying to convert this code
def password_reset_instructions(user)
subject "Registered"
recipients user.email
body :edit_password_reset_url => edit_password_reset_url(user.perishable_token)
end
to this code
def password_reset_instructions(user)
#user = user
mail(:to => user.email, :subject => "Registered")
end
My problem is i don't know where to put the code below.
:edit_password_reset_url => edit_password_reset_url(user.perishable_token)"
I am using authlogic on rails 3.
In Rails 3, Mailers work just like controllers. You can use the instance variable of the user in the accompanying view.
Not tested, but try this:
def password_reset_instructions(user)
#edit_password_reset_url = edit_password_reset_path(user.perishable_token)
mail(
:subject => "Password Reset Instructions",
:recipients => user.email
)
end