I'm using devise confirmable. I want to give the user a link to click and resend the confirmation email. Problem is, when the user clicks the link, it isn't going to the devise controller. Is there something I'm missing in the routes.rb file? Here is my setup:
routes.rb
devise_for :users, :controllers => { :registrations => "registrations", :sessions => "sessions", :omniauth_callbacks => "authentications" }
user.rb
devise :omniauthable, :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
The view:
Resend confirmation
Thanks
to send confirmation instructions to the user you find the user and then just user.send_confirmation_instructions
namespace :user do
task :resend_confirmation => :environment do
users = User.where('confirmation_token IS NOT NULL')
users.each do |user|
user.send_confirmation_instructions
end
end
end
resource_params
is a method defined at devise controller which gets the controller resources specific do device resource (User) for example. definition in DeviseController
def resource_params
params.fetch(resource_name, {})
end
so in order to pass the email as a parameter you neet to include it in a user hash, so in the view instead of
link_to('resend', user_confirmation_path(email: "user#example.com"), :method => :post)
insert the email in a Hash
link_to('resend', user_confirmation_path(user: {:email => "user#example.com"}), :method => :post)
this way devise will pick up the email parameter
Pretty old post. Though, instead of sending the instructions directly, you might just want to point the user to Devise's workflow:
= link_to 'Resend confirmation', new_user_confirmation_path
That'll take the user to Devise's view requesting the email to send the confirmation instructions.
Hope it helps anyone, anyway. :)
I am sharing my solution as it's a bit of a different take, but closer to the normal user flow in my opinion (redirect to thanks page where you have a button to resend without writing email again), and you won't have to override the confirmations controller.
After signing up, the user get redirected to a 'Thanks' page. In registrations_controller:
def after_inactive_sign_up_path_for(resource)
session[:user_id] = resource.id
redirect_to thanks_url
end
in users controller, you just use the .send_confirmation_instructions on user
def thanks
#user = User.find_by(id: session[:user_id])
end
def resend_confirmation
user = User.find_by(id: params[:user_id])
user.send_confirmation_instructions
end
routes:
get '/thanks', to: 'users#thanks'
post '/resend_confirmation', to: 'users#resend_confirmation', as: 'resend_confirmation'
finally, in the 'thanks' view:
<%= button_to "Resend confirmation", resend_confirmation_path(:user_id => #user.id) %>
This could be cleaned up a bit, I'm sure, as I've just wrote it and I'm still new at Rails, but I was looking for this kind of solutions on Stack and could not find it so I thought to share it.
As you could see on https://github.com/plataformatec/devise/blob/master/app/controllers/devise/confirmations_controller.rb#L2, the HTTP method for confirmation#new is GET, not POST. Try to remove 'data-method="post"' and see if it works.
Here is my solution. Hope not missing something in my switch in resource_params.
Note: not ajaxified
confirmation controller
class ConfirmationsController < Devise::ConfirmationsController
# POST /resource/confirmation
def create
# self.resource = resource_class.send_confirmation_instructions(resource_params)
self.resource = resource_class.send_confirmation_instructions({email: current_user.email})
if successfully_sent?(resource)
respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
else
respond_with(resource)
end
end
protected
# The path used after resending confirmation instructions.
def after_resending_confirmation_instructions_path_for(resource_name)
root_path
end
end
routes
devise_for :users, controllers: { confirmations: "confirmations" }
view
<%= link_to "resend confirmation", user_confirmation_path, data: { method: :post } %>
To resend the confirmation email, you want the post method with just 'users/confirmation'--no 'new' at the end.
Here's a form that requests the user's email and submits the confirmation resend request to Devise as follows:
form_for(resource, url: user_confirmation_path) do |f|
.form-inputs
= f.input :email
.form-actions
= f.button :submit, "Resend"
here is my solution that will open devise form where user can enter email address and request new email with confirmation token. All devise logic regarding email verification is preserved:
app/controllers/confirmations_controller.rb
class ConfirmationsController < Devise::ConfirmationsController
# GET /resource/confirmation/new
def new
self.resource = resource_class.new
end
end
config/routes.rb
devise_for :users, controllers: { confirmations: "confirmations" }
app/views/devise/confirmations/new.html.erb
<h2>Resend confirmation instructions</h2>
<%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.submit "Resend confirmation instructions" %></div>
<% end %>
<%= render "devise/shared/links" %>
Related
I have an admin namespaced route for a custom dashboard in my app. I have at least 8 models working, with error free crud operations--all except one. I am using the Devise Gem for user management and a User model. I have the User model in the admin namespace and the only operation I can get to work is changing role and destroy, but I can't create a new user from the dashboard. When I try to create a new user; I get the error "You are already signed in.".
controllers/admin/users_controller.rb
class Admin::UsersController < ApplicationController
before_action :authenticate_user!
before_action :admin_only, :except => :show
def index
#users = User.all
end
def show
#user = User.find(params[:id])
unless current_user.admin?
unless #user == current_user
redirect_to root_path, :alert => 'Access denied.'
end
end
end
def create
#user = User.new
end
def update
#user = User.find(params[:id])
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => 'User updated.'
else
redirect_to admin_users_path, :alert => 'Unable to update user.'
end
end
def destroy
#user = User.find(params[:id])
user.destroy
redirect_to admin_users_path, :notice => 'User deleted.'
end
private
def admin_only
unless current_user.admin?
redirect_to root_path, :alert => 'Access denied.'
end
end
def secure_params
params.require(:user).permit!
end
end
models/user.rb
class User < ApplicationRecord
enum role: [:user, :admin]
def set_default_role
self.role ||= :user
end
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
views/admin/users/new.html.erb
<%= form_for User.new do |f| %>
<div class="form-group">
<%= f.label :first_name %><br/>
<%= f.text_field :first_name, autofocus: true %>
</div>
<div class="form-group">
<%= f.label :last_name %><br/>
<%= f.text_field :last_name %>
</div>
<div class="form-group">
<%= f.label :email %><br/>
<%= f.email_field :email %>
</div>
<div class="form-group">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br/>
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="from-group">
<%= f.label :password_confirmation %><br/>
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="form-group" style="margin-top: 1em">
<%= f.submit 'Sign up', class: 'btn btn--primary type--uppercase inner-link' %>
</div>
<% end %>
views/admin/users/new.html.erb (other method)
<%= form_for([:admin, #user]) do |f| %>
This is how all my name spaced forms are setup but this gives me the error First argument in form cannot contain nil or be empty
routes.rb
...
namespace :admin do
get '', to: 'dashboard#index', as: '/'
resources :pages
resources :articles
resources :categories
resources :tags
devise_for :users
resources :users
resources :albums
resources :banners
resources :products do
resources :variations
end
end
...
using the registration#create action is the appropriate architecture for this?
Because that action is built on a different concept, that the user is not logged in and there are a series of checks for that.
views/admin/users/new.html.erb
you are using registration#new controller action, which does not have the #admin valorized.
def new
#admin = Admin.find(params[:admin_id])
....
end
That is why you get the error, also this approach is not correct.
User will need to register with the traditional devise router, then you will also have to create a nested router for admins to create new users.
this is your link to the admin user registration#new
Your current view/controller#action has the #admin = Admin.find(params[:id]) valorized and the view has the following link
link_to new_admin_user_path(:admin, :user)
the new_admin_user_path is for url /admin/:admin_id/users/sign_up(.:format) that you define in your nested routes.
Step 2) decide which controller#action to use
Do you want to use the standard users/registrations#create or use a new action for this?
I believe you should enhance users/registrations#create by generating the controllers actions in your app as described in devise guide
then in devise controller registrations
def new
#admin = Admin.new(:params[:admin_id]) if params[:admin_id].present?
end
your registrations#new and #create will still trigger errors, you will have to read how devise creates this users by reading their amazing sourcecode and rdoc documentation, modify the process accordingly so that admins can create users by using that action otherwise the less DRY alternative is creating a new controller#action and using it to call an existing User method or a method you will create in the User model to create users. In the end it a User is just an entry in your users database table. Just creating in a similar fashion to other users. As the admin is creating the temporary password, encryption/security issues are not anymore that important. The User will have to change the password anyway.
I am trying to update only email using this form, but validation is failing Current password can't be blank, requires password_confirmation, I don't have a clue how to void this requirement
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => {:method => :put, :id=>"email_form"}) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %>
<br/>
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.submit "Update" %></div>
<% end %>
UPDATE
This is my update action in the overrided controller RegistrationsController < Devise::RegistrationsController
class RegistrationsController < Devise::RegistrationsController
def update
#user = User.find(current_user.id)
successfully_updated = if needs_password?(#user, params)
#user.update_with_password(params[:user])
else
# remove the virtual current_password attribute update_without_password
# doesn't know how to ignore it
params[:user].delete(:current_password)
#user.update_without_password(params[:user])
end
if successfully_updated
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
private
def needs_password?(user, params)
user.email != params[:user][:email] ||
params[:user][:password].present?
end
end
Just point to other controller than devise's own registration controntroller, with your own logic for this update action.
You can also override devise registration's controller (read devise docs).
Example:
# config/routes.rb
resource :profile # Singular resource and profile
# profiles_controller
def show
# render your view with the for
end
def update
current_user.update(params[:user]) # if rails < 4 use update_attributes instead
end
# _form.html.erb
<%= form_for(current_user, url: profile_path, html: { method: 'PUT' }) do |f| %>
...
For the second option, overriding devise's own registration controller, I don't really like this approach because in this case you are not really dealing with registrations but an already registered user account:
https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password
After your edit:
I see you are using the second option. Take a look a the needs_password? method in the controller
I've got a pretty standard User model with Devise. Administrators are controlled with an :admin boolean on the model, and users who aren't administrators cannot manage themselves (i.e., only administrators can make changes to users).
What I'd like is to permit users to reset their password if they forget it. They would enter their email address then be emailed a token which would grant them access to change it. I've started a solution but I really don't know how to proceed.
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
attr_accessor :current_password
attr_accessible :name, :password, :password_confirmation, :current_password, :email, :remember_me, :ftp, :colour1, :colour2, :logo, :logo2, :address, :url, :disclosure, :general_advice, :facebook, :twitter, :brand_id, :analytics
attr_protected :admin
validates_uniqueness_of :email, :ftp
belongs_to :brand
has_many :docs
has_many :orders, :through => :docs
has_attached_file :logo
has_attached_file :logo2
end
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new #guesting
if user.admin?
can :manage, :all
else
can :manage, :recoverable
end
end
end
And here's the method:
def reset
#idiot = User.where(:email => params[:email]).first
unless #idiot.nil?
Notifier.send_reset_notice(#idiot).deliver
#idiot.send_reset_password_instructions
redirect_to new_user_session_url
else
redirect_to new_user_session_url, :flash => { :error => "That email address matches no user." }
end
end
The user receives the Devise email but clicking on the link takes the user to the application root and not to a password reset form. I'm not sure how to proceed from here. Any thoughts? Cheers!
UPDATE
Relevant routes:
devise_for :users, :path => "d"
devise_scope :user do
get '/sign_in' => 'devise/sessions#new'
get '/sign_out' => 'devise/sessions#destroy'
end
First, create controller for handle Devise::PasswordsController
class PasswordusersController < Devise::PasswordsController
prepend_before_filter :require_no_authentication
append_before_filter :assert_reset_token_passed, :only => :edit
def new
super
end
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
if successfully_sent?(resource)
redirect_to root_path, :notice => "Instruction has been send to your email"
else
respond_with(resource)
end
end
def edit
super
end
def update
self.resource = resource_class.reset_password_by_token(resource_params)
if resource.errors.empty?
resource.unlock_access! if unlockable?(resource)
flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
set_flash_message(:notice, flash_message) if is_navigational_format?
sign_in(resource_name, resource)
redirect_to login_path, :notice => "Password has been change"
else
respond_with resource
end
end
protected
def after_sending_reset_password_instructions_path_for(resource_name)
root_path
end
def assert_reset_token_passed
super
end
def unlockable?(resource)
super
end
end
Than, run generate view for devise
copy new.html.erb and edit.html.erb from views/devise/passwords to views/passwordusers
on routes.rb you can config such as :
devise_scope :user do
get '/reset_password' => "passowrdusers#new", :as => :reset_password
get '/new_password' => "passwordusers#edit", :as => :new_password
end
edit link on devise/mailer/reset_password_instructions.html.erb
<p><%= link_to 'Change My Password', new_password_path(#resource, :reset_password_token => #resource.reset_password_token) %></p>
finally, on view form login add this
<%= link_to "Forgot Password?", reset_password_url(resource_name) %>
UPDATE
on routes.rb you can config such as :
devise_scope :user do
get '/reset_password' => "passowrdusers#new", :as => :reset_password
get '/new_password' => "passwordusers#edit", :as => :new_password
post '/send_email' => 'passwordusers#create', :as => :create_password
put '/change' => 'passwordusers#update', :as => :update_password
end
on views/passwordusers/new.html.erb
<%= form_for("user", :url => create_password_path(resource_name), :html => { :method => :post }) do |f| %>
on views/passwordusers/edit.html.erb
<%= form_for("user", :url => update_password_path(resource_name), :html => { :method => :put }) do |f| %>
There are similar questions but I've gone over them and none of them have applied.
I am using Devise and Devise invitations. Rails 3.2.6.
The tricky thing is I have two user models. User and a single table inheritance setup where Artist inherits from User. I have two invitations controllers to allow sending invitations separately to both Users and Artists.
The issue right now is that artists are not able to accept their invitations. Upon form submit of updating their password, they get a 406 unacceptable error.
Relevant Code:
Routing:
devise_for :users,
:class_name => User,
:controllers => { :invitations => 'user/invitations' }
namespace :artist do
# Special route for devise, artists invitation
devise_scope :user do
resource :invitation, :controller => :invitations, :only => [:create, :new, :update] do
get :accept, :action => :edit
end
end
end
Artist invitations controller
class Artist::InvitationsController < Devise::InvitationsController
respond_to :html
# PUT /resource/invitation
def update
self.resource = Artist.accept_invitation!(params[resource_name])
if resource.errors.empty?
resource.pending
flash[:notice] = "Welcome, complete your artist agreement to get started"
sign_in(:user, resource)
respond_with resource, :location => after_accept_path_for(resource)
else
respond_with_navigational(resource){ render :edit }
end
end
end
Form description. edit.html.erb.
<%= form_for resource, :as => resource_name, :url => artist_invitation_path(resource_name), :html => { :method => :put } do |f| %>
<%= f.hidden_field :invitation_token %>
<%= f.label :password, class: 'control-label' %>
<%= f.password_field :password, :placeholder => 'Enter a password' %>
<%= f.label :password_confirmation, class: 'control-label' %>
<%= f.password_field :password_confirmation, :placeholder => 'Confirm your password' %>
<%= f.submit "Set my password", :'data-disable-with' => "Hang on..." %>
<% end %>
Accept headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:236
Content-Type:application/x-www-form-urlencoded
Returns
Request URL:http://localhost:3000/artist/invitation.user
Request Method:POST
Status Code:406 Not Acceptable
I can see that something in the format must be off because of the response adding in that .user to the end. I can not for the life of me see why or where that is happening. Please let me know if there's any info I left out which could help.
Got it, finally.
The key is to look precisely at what resource_name is giving you throughout the form and controller actions. In my case it continued to fallback to :user when it needed to be :artist and as Frost mentioned in a comment, the generated form html changes greatly if you pay attention to where resource_name is used. The update action in the controller needed to be overridden to use :artist directly and the form_for needed to be tweaked to remove resource_name from arguments and the :as argument.
I am new with rails and i am using "devise" gem for authentication purposes.
At first i add a new user through default sign up page (E.g./users/sign_up)
Then, i made "sign_up" page only available to signed_in users by following instructions from
Devise before filter that prevents access to "new_user_registration_path" unless user is signed-in
Now, after sign in process when i try open sign up page it always directs me to root_path! How can i access sign up page?
My "roots.rb" file as follows:
Example::Application.routes.draw do
devise_for :users, :controllers => { :registrations => 'registrations'}
resources :companies
resources :orders
resources :customers
root :to => "welcome#index"
end
Thank you all!
I have other decision. Bitterzoet said
As you can see in the devise source if you navigate to the sign_up it executes the before_filter require_no_authentication and this redirects to the root path which you can find here.
You don't need override registration_controller, you can change only your custom registration_controller that echo original registration_controller.
class Admin::RegistrationsController < Devise::RegistrationsController
layout 'admin'
prepend_before_filter :require_no_authentication, :only => []
prepend_before_filter :authenticate_scope!
end
If you are getting redirected it probably means you are not properly authenticated when you navigate to that page, seeing as it requires a valid user session.
Please post your Registrations controller file as well.
If you are getting redirected it probably means you are not properly authenticated when you navigate to that page, seeing as it requires a valid user session.
Please post your Registrations controller file as well.
Addition:
As you can see in the devise source if you navigate to the sign_up it executes the before_filter require_no_authentication and this redirects to the root path which you can find here.
I think you will have to explicitly override the registrations_controller that I linked first if you really want to override this behaviour :-)
The way I handled the scenario of being able to create New Users if you are signed in was by generating a User controller and having new and create action methods that would save to the User model. (This was stripped from a current app I'm working on so hopefully I didn't miss anything)
user_controller.rb
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Successfully created User."
redirect_to root_path
else
render :action => 'new'
end
end
views/user/new.html.erb
<%= form_for #user, :url => user_index_path do |f| %>
<p><%= f.label :email %>
<%= f.text_field :email %></p>
<p><%= f.label :password %>
<%= f.password_field :password %></p>
<p><%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %></p>
<p><%= f.submit "Save" %></p>
<% end %>
config/routes.rb (Rails 3)
resources :user, :controller => "user"
Link to the New User page
<%= link_to 'New User', new_user_path %>