Change flash error from Devise confirmation - ruby-on-rails

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

Related

Custom devise method : render 'new' redirect to /users instead of /users/sign_up

I have a custom protected method in my devise registration_controller.rb :
before_action :check_secret_code, only: [:create]
# Method de condition pour création du compte
include ApplicationHelper
# need application_helper.rb or will not work.
# GET /resource/sign_up
def new
super
end
# POST /resource
def create
super
end
def check_secret_code
if Invitation.where(:invit_name => params[:user]["invit_ref"]).blank?
render 'new'
else
Invitation.where(:invit_name => params[:user]["invit_ref"]).destroy_all
return true
end
end
But when the render 'new' is used, it's render /users instead of /users/sign_up with no persistant infos. Do you know why ?
Here is my application_helper.rb
module ApplicationHelper
def resource_name
:user
end
def resource
#user ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
end
render 'new' renders new template, but url and action stay the same.
For example, you call render 'new' from create method:
def create
#model = Model.new(params[:model])
if #model.valid?
#model.save!
redirect_to :some_path
else
render 'new'
end
end
if #model is not valid, it renders new template with #model variable from create method.
Thus, in your case, if you want the user to be redirected to users/sign_up you need to explicitly redirect him to this page:
Either by redirect_to 'users/sign_up' or by using a path helper method.

Rails doesn’t persist flash message key after destroy action

I use Rails 4, and have four methods at my application_controller.rb for setting flash messages in Rails
def exclusion_info_for model_name
flash[:notice] = "#{model_name.to_s.capitalize} has been deleted."
end
def creation_notice_for model_name
flash[:notice] = "#{model_name.to_s.capitalize} has been created."
end
def update_notice_for model_name
flash[:notice] = "#{model_name.to_s.capitalize} has been updated."
end
def error_notice
flash[:error] = "An unexpected error has it occurred"
end
But the flash setting at exclusion_notice_for is lost after redirection of the action destroy. The others methods works normally.
The Controller
class CustomersController < ApplicationController
respond_to :html
def new
respond_with #customer = customer
end
def create
if #customer = Customer.create(customer_attrs)
creation_notice_for :customer
else
error_notice
end
respond_with #customer, location: "/"
end
def show
respond_with #customer = customer
end
def index
respond_with #customers = Customer.all
end
def edit
respond_with #customer = customer
end
def update
if #customer = customer.update(customer_attrs)
update_notice_for :customer
else
error_notice
end
respond_with #customer, location: "/"
end
def destroy
if #customer = customer.destroy()
exclusion_info_for :customer
else
error_notice
end
respond_with #customer, location: "/"
end
private
def customer
id ? Customer.find(id) : Customer.new
end
def customer_attrs
params.require(:customer).permit(:name)
end
end
This is the application destroy button currently genereted
This is the application.rb file
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :null_session
# protect_from_forgery with: :exception
include FormattedTime
def form_parent
ObjectSpace._id2ref(params[:form_parent_object_id].to_i) if params[:form_parent_object_id]
end
helper_method :form_parent
def root
render "layouts/application", layout: false
end
protected
def id
params[:id]
end
# refactored
def info_flashed model_name, action
flash[:notice] = "#{model_name.to_s.capitalize} has been #{action}."
end
def error_notice
flash[:error] = "An unexpected error has it occurred"
end
end
Flashing Method will work all of your actions simply add 'created' or 'updated' string like in destroy method.
You had error Can't verify CSRF token authenticity
You can put this method in application.rb file to fix it all.
protect_from_forgery with: :exception, if: Proc.new { |c| c.request.format != 'application/json' }
protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }
protected
def info_flashed (model_name, action)
flash[:notice] = "#{model_name.to_s.capitalize} has been #{action}."
end
In Controller
def destroy
if #customer = customer.destroy
info_flashed (#customer, 'deleted')
else
error_notice
end
respond_with #customer, location: "/" # you need to redirect correct path.
end

Rails how to require the current password to allow user update

I am having a rather difficult problem i want to update the user profile only if they submit the current password.I am not using devise.And another post here at stack overflow didn't really help me.
This is my User controller code:
class UsersController < ApplicationController
def new
#user = User.new
end
def show
#user = User.find(params[:id])
#user_posts = #user.posts if #user
if #user
if #user_posts
render 'show.html'
else
render 'show.html'
end
else
render file: 'public/404.html', status: 404, formats: [:html]
end
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to root_path
flash[:notice] = "Successfully Signed up :-)"
else
redirect_to signup_path
flash[:notice] = "You didn't sign up successfully :-("
end
end
def edit
#user = User.find(params[:id])
if current_user.id = #user.id
render 'edit'
else
redirect_to #user
end
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:notice] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
:password == :password_confirmation
private
def user_params
params.require(:user).permit(:user_name, :email, :password, :password_confirmation)
end
end
And this is my user.rb:
class User
has_secure_password
has_many :posts
has_many :comments
def admin?
self.role == 'admin'
end
def moderator?
self.role == 'moderator'
end
end
Please help because I have been working with this for a long time now. And the other solution about this topic here at stack overflow didn't work.
One way is to use virtual attributes
1. The User model
class User < ActiveRecord::Base
attr_accessor :current_password
end
2. The form
add the current_password attribute to the form as a text_field input
3. The UsersController
def update
#user = User.find params[:id]
if #user.authenticate(update_params[:current_password])
# update the user
# maybe check if the data are valid
#user.update(update_params)
else
flash[:warning] = "Please provide your password"
#user.errors.add :current_password, "invalid"
render :edit
end
end
def update_params
params.require(:user).permit(:current_password, :email)
end
First, you have a problem in your edit action:
current_user.id = #user.id
That assigns #user.id to current_user.id - you wanted == to test that it's the correct User. You should put a similar check on update, and probably extract it into a before_action so you can easily apply it anywhere you want to.
To check that the password is present, add it to your form like any other field and then get it out of params to verify it. That would look something like this:
class UsersController < ApplicationController
def update
encrypted = encrypt(params[:password]) # Using whatever your mechanism is
if encrypted == #user.encrypted_password
# Update the user
else
flash[:notice] = 'Password is required to update user information.'
redirect_to edit_user(path(#user))
end
end
end

Error-undefined method `add_error_on' for ConfirmationsController:Class-rails4-devise

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

Uninitialized Constant ConfirmationsController

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.

Resources