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.
Related
I'm using devise to manage authentication. I have a User model and Admin model. I want to be able to allow both users and admins soft delete user accounts.
I have implemented the soft delete for users and everything works well, however, adding functionality for admins results in a 401 unauthorized and a redirect to the user sign in page. I'm not exactly sure how to get around this.
So far I have:
config/routes.rb
...
devise_for :users
devise_scope :user do
resources :users, only: [:destroy], controller: 'members/registrations', as: :user_registration do
get 'cancel'
end
end
...
controllers/members/registrations_controller.rb
class Members::RegistrationsController < Devise::RegistrationsController
def destroy
#user = User.find(params[:id])
not_authorized unless authorized?
#user.soft_delete
user_post_destroy if is_current_user?
end
private
def authorized?
if signed_in?
is_current_user?
else
session[:session_id] == #user.author_session_token
end
end
def not_authorized
flash[:error] = t('errors.messages.not_authorized')
flash.keep
redirect_back(fallback_location: root_path)
end
def user_post_destroy
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
set_flash_message :notice, :destroyed
yield resource if block_given?
respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
end
def is_current_user?
#user == current_user
end
end
models/user.rb
...
def soft_delete
update_attribute(:deleted_at, Time.current)
end
def active_for_authentication?
super && !deleted_at
end
def inactive_message
!deleted_at ? super : :deleted_account
end
...
Devise sends to mail instruction for account confirmation. When the user clicks on the Confirmation link, their account is activated and the user gets redirected to the static_pages#home page, but i need that this link redirect to other page.
Confirmation url:
<p><%= link_to 'Подтвердить мой аккаунт', confirmation_url(#resource, confirmation_token: #token) %></p>
confirmation_controller:
class Users::ConfirmationsController < Devise::ConfirmationsController
# GET /resource/confirmation/new
def new
super
end
# POST /resource/confirmation
def create
super
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
super
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)
new_profiles_path (resource_name, resource)
end
end
New error in browser:
/home/vlad/Desktop/MyApp/app/controllers/users/confirmations_controller.rb:26:
syntax error, unexpected ',', expecting ')' new_profiles_path
(resource_name, resource) ^
You dont need to change confirmation_url instead you need to override after_confirmation_path_for
To do that you need to define after_confirmation_path_for in ConfirmationsController
To do that create confirmations_controller.rb in app/controllers directory:
class ConfirmationsController < Devise::ConfirmationsController
private
def after_confirmation_path_for(resource_name, resource)
your_new_after_confirmation_path
end
end
In config/routes.rb, add this line so that Devise will use your custom ConfirmationsController.
devise_for :users, controllers: { confirmations: 'confirmations' }
Restart the web server, and you should have it.
Please refer Devise Wiki for more options/details
updated
Here is confirmations_controller.rb
class Users::ConfirmationsController < Devise::ConfirmationsController
# GET /resource/confirmation/new
def new
super
end
# POST /resource/confirmation
def create
super
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
super
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)
new_profiles_path(resource_name, resource)
end
end
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.
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