I have recently added confirmable to my rails application with devise. It is all working smoothly however when I create a new user I am getting the
signed_up: "Welcome! You have signed up successfully."
rather than
signed_up_but_unconfirmed:"A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
even though they are unconfirmed. Was there something I needed to do when adding confirmable to change this? Thanks!
UPDATE
I am using my own user controller and routing it like so below:
routes.rb
devise_for :users, :controllers => {:registrations => "users"}
devise_scope :user do
get 'users/:id' => 'users#show', as: "user"
end
You could have your routes with this
as :user do
patch '/users/confirmation' => 'users/confirmations#update', :via => :patch, :as => :update_user_confirmation
end
and have the confirmations_controller with the following:
class Users::ConfirmationsController < Devise::ConfirmationsController
skip_before_filter :authenticate_user!
before_action :check_if_current_user
# PUT /resource/confirmation
def update
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
#confirmable.attempt_set_password(params[:user])
if #confirmable.valid? && #confirmable.password_match?
do_confirm
else
do_show
#confirmable.errors.clear #so that we wont render :new
end
else
self.class.add_error_on(self, :email, :password_already_set)
end
end
if !#confirmable.errors.empty?
render 'devise/confirmations/new' #Change this if you don't have the views on default path
end
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
do_show
else
do_confirm
end
end
if !#confirmable.errors.empty?
self.resource = #confirmable
render 'devise/confirmations/new' #Change this if you don't have the views on default path
end
end
protected
def with_unconfirmed_confirmable
original_token = params[:confirmation_token]
confirmation_token = Devise.token_generator.digest(User, :confirmation_token, original_token)
#confirmable = User.find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
if !#confirmable.new_record?
#confirmable.only_if_unconfirmed { yield }
end
end
def do_show
#confirmation_token = params[:confirmation_token]
#requires_password = true
self.resource = #confirmable
render 'devise/confirmations/show' #Change this if you don't have the views on default path
end
def do_confirm
#confirmable.confirm!
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, #confirmable)
end
def check_if_current_user
if current_user.present?
render :file => "#{Rails.root.to_s}/public/confirmed.html", :status => :unauthorized
end
end
end
Hope it is helpful.
Related
In my app I am using Devise and Active Admin. Users are created in admin area without password, then they receive a mail with a link to a confirmation page where they can enter the password for the new account.
This is confirmation controller:
class ConfirmationsController < Devise::ConfirmationsController
def show
#original_token = params[:confirmation_token]
digested_token = Devise.token_generator.digest(self, :confirmation_token,params[:confirmation_token])
self.resource = resource_class.find_by_confirmation_token(digested_token) if params[:confirmation_token].present?
super if resource.nil? or resource.confirmed?
render :layout => "internal"
end
def confirm
digested_token = Devise.token_generator.digest(self, :confirmation_token, params[resource_name][:confirmation_token])
self.resource = resource_class.find_by_confirmation_token(digested_token) if params[resource_name][:confirmation_token].present?
if resource.update_attributes(params[resource_name].except(:confirmation_token).permit(:email, :password, :password_confirmation)) && resource.password_match?
self.resource = resource_class.confirm_by_token(params[resource_name][:confirmation_token])
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, resource)
else
#original_token = params[resource_name][:confirmation_token]
render :action => "show", :layout => "internal"
end
end
end
Relevant routes:
devise_for :users, :path_prefix => 'd', :controllers => {:confirmations => 'confirmations'}
devise_scope :user do
patch "/confirm" => "confirmations#confirm"
end
When the users clicks on the activation link in the background it sets the account as confirmed in the database, but instead of being redirected to confirmations/show to set the password, I get on this line render :layout => "internal"
Render and/or redirect were called multiple times in this action.
Please note that you may only call render OR redirect, and at most
once per action. Also note that neither redirect nor render terminate
execution of the action, so if you want to exit an action after
redirecting, you need to do something like "redirect_to(...) and
return".
Why is this happening?
In your ConfirmationsController:
class ConfirmationsController < Devise::ConfirmationsController
def show
#original_token = params[:confirmation_token]
digested_token = Devise.token_generator.digest(self, :confirmation_token,params[:confirmation_token])
self.resource = resource_class.find_by_confirmation_token(digested_token) if params[:confirmation_token].present?
# here you call super if resource is nil or confirmed
# super calls the show action of Devise::ConfirmationController
# it results with the first render (but doesn't call any return)
super if resource.nil? or resource.confirmed?
# after super is finished you continue and render layout internal
# (again, since it has happened in the parent controller already)
render :layout => "internal"
end
...
you can check what exactly is being done in Devise::ConfirmationsController on their github page: https://github.com/heartcombo/devise/blob/master/app/controllers/devise/confirmations_controller.rb
if you want to render render the confirmations/show page then just remove the
render layout: 'internal'
line.
I create new user. After creating should received confirmation mail. but when i open mailcatcher(http://127.0.0.1:1080/)... nothing!
i use mailcatcher v. 0.5.12, Rails 3.2.22
In development.rb added:
# Don't care if the mailer can't send
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 }
config.action_mailer.raise_delivery_errors = true
What is wrong with settings? How check error? Please, help, thank you.
UPDATE
Confirmationcontroller:
class ConfirmationsController < Devise::ConfirmationsController
def new
super
resource.login = current_user.email if user_signed_in?
end
protected
def after_resending_confirmation_instructions_path_for(resource_name)
if user_signed_in?
home_path
else
new_session_path(resource_name)
end if is_navigational_format?
end
# The path used after confirmation.
def after_confirmation_path_for(resource_name, resource)
'/profile'
end
end
userscontrollers.rb:
# encoding: utf-8
class UsersController < ApplicationController
before_filter :require_admin, only: [:add_moderator, :remove_moderator, :destroy]
before_filter :require_moderator, only: [:edit, :update, :paid_on, :paid_off,
:ban, :unban]
before_filter :authenticate_user!, except: [:new, :create]
before_filter :load_user, only: [:show, :photos, :videos, :audios,
:buy_rating, :do_buy_rating,
:add_moderator, :remove_moderator,
:edit, :update, :paid_on, :paid_off, :ban, :unban, :destroy,
:add_funds, :mute, :unmute]
layout :determine_layout
def new
#user = User.new
#invite = Invite.find_by_code(session[:invite]) if session[:invite].present?
#user.email = #invite.email if #invite
end
def create
#user = User.new(params[:user])
raise ActiveRecord::RecordInvalid.new(#user) unless verify_recaptcha(model: #user, message: 'message')
#invite = Invite.find_by_code(session[:invite]) if session[:invite].present?
User.transaction do
#user.save!
#user.current_password = #user.password
#user.theme_ids = params[:user][:theme_ids]
#user.group_ids = [114, 130]
#user.save!
end
if #invite
#invite.new_user = #user
#invite.use!
end
redirect_to #user
rescue ActiveRecord::RecordInvalid
render :new
end
.........................................................
.........................................................
private
def determine_layout
return 'welcome' if %w(new create).include?(params[:action])
return 'dating' if params[:action]=='search'
'inner'
end
end
registrationscontroller.rb:
# encoding: utf-8
class RegistrationsController < Devise::RegistrationsController
def create
if verify_recaptcha
super
else
flash.delete :recaptcha_error
build_resource
resource.valid?
resource.errors.add(:base, :invalid_recaptcha)
# clean_up_passwords(resource)
render :new
end
rescue ActiveRecord::RecordNotUnique
render :new
end
def update
redirect_to '/settings'
end
# def update
# # required for settings form to submit when password is left blank
# if params[:user][:password].blank?
# params[:user].delete("password")
# params[:user].delete("current_password")
# end
#
# #user = User.find(current_user.id)
# if update_user
# set_flash_message :notice, :updated
# # Sign in the user bypassing validation in case his password changed
# sign_in #user, :bypass => true
# redirect_to after_update_path_for(#user)
# else
# render "edit"
# end
# end
def edit
redirect_to '/settings'
end
def destroy
current_password = params[:user].delete(:current_password)
if resource.valid_password?(current_password)
resource.mark_as_deleted!
render inline: "$('body').fadeOut(3000, function() { document.location = 'http://ya.ru'; })"
else
render inline: "$.flash.error('error')"
end
# Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
# set_flash_message :notice, :destroyed if is_navigational_format?
# respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
end
protected
def after_sign_up_path_for(resource)
'/profile'
end
end
Code was without Devise.mail_confirmation_instructions(#user).deliver. I realized, now all in fine
I am using Rails 4 and devise 3.2. At sign up time, an activation/confirmation link is sent by e-mail to the newly registered user. I have implemented this from this article in the Devise wiki.
Whenever I put same passwords they login without any problem, but it's not working when I make a error of password doesn't match in 1st attempt and put same password in 2nd attempt.
This is the error which shows up
NoMethodError in ConfirmationsController#update
undefined method `add_error_on' for ConfirmationsController:Class
confirmation controller
class ConfirmationsController < Devise::ConfirmationsController
skip_before_filter :require_no_authentication
skip_before_filter :authenticate_user!
# PUT /resource/confirmation
def update
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
#confirmable.attempt_set_password(params[:user])
if #confirmable.valid? and #confirmable.password_match?
do_confirm
else
do_show
#confirmable.errors.clear #so that we wont render :new
end
else
self.class.add_error_on(self, :email, :password_already_set)
end
end
if !#confirmable.errors.empty?
render 'devise/confirmations/new'
end
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
do_show
else
do_confirm
end
end
if !#confirmable.errors.empty?
self.resource = #confirmable
render 'devise/confirmations/new'
end
end
protected
def with_unconfirmed_confirmable
original_token = params[:confirmation_token]
confirmation_token = Devise.token_generator.digest(User, :confirmation_token, original_token)
#confirmable = User.find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
if !#confirmable.new_record?
#confirmable.only_if_unconfirmed {yield}
end
end
def do_show
#confirmation_token = params[:confirmation_token]
#requires_password = true
self.resource = #confirmable
render 'devise/confirmations/show'
end
def do_confirm
#confirmable.confirm!
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, #confirmable)
end
end
Please help. Thank you.
The error case for the update action in that wiki article is out of date. There is no longer an add_error_on method in Devise. Instead, you can just use the basic ActiveRecord errors object:
#confirmable.errors.add(:email, :password_already_set)
Then, you'll still have a problem, because it needs to assign #confirmable to resource in order for the default views to work correctly (the wiki example does this for all the other error renders, just not this one):
if !#confirmable.errors.empty?
self.resource = #confirmable
render 'devise/confirmations/new'
end
I'm using this guide from Devise, to set the ability of the user to create an account without a password, but to set it later on confirmation.
However errors are being showed through the <%= devise_error_messages! %> excerpt. I want to change the controller, so as it's show on the usual flash from Rails.
How can I do that?
The controller I'm overriding is as follow:
# app/controllers/confirmations_controller.rb
class ConfirmationsController < Devise::ConfirmationsController
layout "login"
# Remove the first skip_before_filter (:require_no_authentication) if you
# don't want to enable logged users to access the confirmation page.
skip_before_filter :require_no_authentication
skip_before_filter :authenticate_user!
# PUT /resource/confirmation
def update
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
#confirmable.attempt_set_password(params[:user])
if #confirmable.valid?
do_confirm
else
do_show
#confirmable.errors.clear #so that we wont render :new
end
else
self.class.add_error_on(self, :email, :password_allready_set)
end
end
if !#confirmable.errors.empty?
render 'devise/confirmations/new' #Change this if you don't have the views on default path
end
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
do_show
else
do_confirm
end
end
if !#confirmable.errors.empty?
self.resource = #confirmable
render 'devise/confirmations/new' #Change this if you don't have the views on default path
end
end
protected
def with_unconfirmed_confirmable
#confirmable = User.find_or_initialize_with_error_by(:confirmation_token, params[:confirmation_token])
if !#confirmable.new_record?
#confirmable.only_if_unconfirmed {yield}
end
end
def do_show
#confirmation_token = params[:confirmation_token]
#requires_password = true
self.resource = #confirmable
render 'devise/confirmations/show' #Change this if you don't have the views on default path
end
def do_confirm
#confirmable.confirm!
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, #confirmable)
end
end
I think you may have error messages and flash messages mixed up. When you get errors being shown through the <%= devise_error_messages! %>, those error messages are coming from the validations that are set on the model. For example in the User model,
validates :password, presence: true
These are different errors compared to the flash messages. You set flash messages in the controller like this
def create
# code .....
if #post.save
flash[:success] = "Post was successfully created"
redirect_to [#investigation, #post]
else
flash.now[:error] = "Post was not created"
render 'new'
end
end
These flash messages just respond to the conditions you specify in the controller actions. Hope this helps
I'm trying to do a custom override of devise confirmations so that a new user creates a password after they receive a confirmation email. This is in the devise wiki and can be found here.
When I navigate to the confirmation link, I am, however confronted by the following error
uninitialized constant ConfirmationsController
I've seen this before when I flubbed the name of a controller class (left out an s or something similar), however I can't find anything like that here. The two relevant files I can think to present are my controller and my routes, relevant to devise.
Here's my controller:
class Users::ConfirmationsController < Devise::ConfirmationsController
# Remove the first skip_before_filter (:require_no_authentication) if you
# don't want to enable logged users to access the confirmation page.
skip_before_filter :require_no_authentication
skip_before_filter :authenticate_user!
# GET /resource/confirmation/new
def new
super
end
# POST /resource/confirmation
# def create
# super
# end
# GET /resource/confirmation?confirmation_token=abcdef
# PUT /resource/confirmation
def update
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
#confirmable.attempt_set_password(params[:user])
if #confirmable.valid? and #confirmable.password_match?
do_confirm
else
do_show
#confirmable.errors.clear #so that we wont render :new
end
else
#confirmable.errors.add(:email, :password_already_set)
end
end
if !#confirmable.errors.empty?
self.resource = #confirmable
render 'devise/confirmations/new' #Change this if you don't have the views on default path
end
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
do_show
else
do_confirm
end
end
unless #confirmable.errors.empty?
self.resource = #confirmable
render 'devise/confirmations/new' #Change this if you don't have the views on default path
end
end
protected
# The path used after resending confirmation instructions.
def after_resending_confirmation_instructions_path_for(resource_name)
super(resource_name)
end
# The path used after confirmation.
def after_confirmation_path_for(resource_name, resource)
super(resource_name, resource)
end
def with_unconfirmed_confirmable
#confirmable = User.find_or_initialize_with_error_by(:confirmation_token, params[:confirmation_token])
if !#confirmable.new_record?
#confirmable.only_if_unconfirmed {yield}
end
end
def do_show
#confirmation_token = params[:confirmation_token]
#requires_password = true
self.resource = #confirmable
render 'devise/confirmations/show' #Change this if you don't have the views on default path
end
def do_confirm
#confirmable.confirm!
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, #confirmable)
end
end
end
And here's the routes relevant to devise:
devise_for :users, controllers: {
sessions: 'users/sessions',
confirmations: "confirmations"
}
as :user do
patch '/user/confirmation' => 'confirmations#update', :via => :patch, :as => :update_user_confirmation
end
Please feel free to ask for any other code that you think might be helpful. Thanks in advance for any ideas.
Shouldn't you be going to 'users/confirmations#update'? not 'confirmations#update' based on your class name of Users::ConfirmationsController
I normally wrap the routes in namespaces, but for simplicity, you should probably update your patch.