Rails Devise: How to access sign up page after signed in? - ruby-on-rails

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 %>

Related

Devise update password route

On a rails app setup with Devise, i am trying to provide users with a form to change passwords.
i have followed the solution 3 from the Devise wiki:https://github.com/heartcombo/devise/wiki/How-To:-Allow-users-to-edit-their-password
and accordingly have in a user controller
class UsersController < Devise::RegistrationsController
def update_password
#user = current_user
if #user.update(user_params)
# Sign in the user by passing validation in case their password changed
bypass_sign_in(#user)
redirect_to root_path
else
render "edit"
end
end
end
routes.rb
devise_for :users,
path: "", path_names: {
sign_in: "login",
sign_out: "logout",
sign_up: "register",
edit: "settings"
},
controllers: {
registrations: "users",
sessions: "users/sessions"
}
resources :users do
patch 'update_password'
end
Rake routes gives me :
user_update_password_path POST (/:locale)/users/:user_id/update_password(.:format)
users#update_password {:locale=>/fr|en|de/}
the link to access the menu is the following:
<%= link_to user_update_password_path(current_user) %>
in browser, that links directs me to :
http://localhost:3000/en/users/1/update_password
but I receive a Routing error
No route matches [GET] "/en/users/1/update_password"
When I wrap the
resources :users do
resources :wishlists
collection do
patch 'update_password'
end
end
the link_to send to
http://localhost:3000/1/password
Which results in the error
undefined local variable or method `user_update_password_path' for
<#:0x00007f86cfe48f10> Did you mean? user_password_path
however, rails routes shows:
update_password_users PATCH (/:locale)/users/update_password(.:format) users#update_password {:locale=>/fr|en|de/}
but a link_to
update_password_users_path
results in an error
Could not find devise mapping for path "/en/users/update_password".
This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example:
devise_scope :user do
get "/some/route" => "some_devise_controller" end
2) You are testing a Devise controller bypassing the router. If so,
you can explicitly tell Devise which mapping to use:
#request.env["devise.mapping"] = Devise.mappings[:user]
What have I missed ?
First of all in the solution 3 it says resource not resources. Watch the differences carefully between yours and the below one -
resource :user, only: [:edit] do
collection do
patch 'update_password'
end
end
Second the like should direct to edit_user_path not update_password_user as that is the patch route.
Third, you have to add a edit action to your controller as the wiki suggests. And also a form for the action.
before_action :authenticate_user!
def edit
#user = current_user
end
and in app/views/users/edit.html.erb
<%= form_for(#user, :url => { :action => "update_password" } ) do |f| %>
<div class="field">
<%= f.label :password, "Password" %><br />
<%= f.password_field :password, :autocomplete => "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="action_container">
<%= f.submit %>
</div>
<% end %>
You missed lots of things. Try reading the wiki again.

Rails with Devise - Login form partial redirects on bad login, logout doesn't reload

I'm currently working on customizing Devise for my own web app.
I'm running into a couple problems with logging in and logging out.
Problem #1:
I've changed the devise/sessions/new.html.erb view to a custom form and turned it into a partial, _new.html.erb
_new.html.erb
<br/><br/>
<h3 id="login-box-label">Sign in here:</h3>
<br/>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<%= f.label :login, "Login:", class: "login-label" %>
<%= f.text_field :login, autofocus: true, class: "login-input" %>
<br/><br/>
<%= f.label :password, "Password:", class: "login-label" %>
<%= f.password_field :password, autocomplete: "off", class: "login-input" %>
<br/><br/>
<%= f.submit "Log in", class: "login-button" %>
<% end %>
I'm rendering this partial on the homepage. It works great when the credentials entered are correct, but when the credentials entered are incorrect, instead of displaying an error, the form submission redirects the user to the regular /devise/sessions/new.html.erb, where the error is shown. I want to avoid this page entirely. Why is it redirecting here, and how can I stop that and make the errors show up in the div in which I'm rendering the partial?
Problem #2:
This may seem like a small issue, but it's not what I want to have happen. When I click my site's Logout button when I'm logged in, I've told the application controller:
def after_sign_out_path_for(user)
root_path
end
Additionally, here is my logout button:
<%= link_to 'Logout', destroy_user_session_path, remote: true, method: "delete", class: "logged-button", id: "logout" %>
However, when I log out from the homepage, the logout is successful, but the homepage doesn't actually reload - it doesn't even turbolink, I assume because it's the same page. I need the page to refresh so that the control panel/logout buttons turn into a login button. What am I missing?
EDIT
I've generated the user session controller for Devise, as suggested by Gokul L, and it looks like this:
class Users::SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]
# GET /resource/sign_in
# def new
# super
# end
# POST /resource/sign_in
# def create
# super
# end
# DELETE /resource/sign_out
# def destroy
# super
# end
# protected
# If you have extra params to permit, append them to the sanitizer.
# def configure_sign_in_params
# devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
# end
end
What bits do I need to add here to get the desired behavior?
Remove comments from create and new action of the sessions_controller and then add then also generate the devise views in your app/views.
Add routes:
devise_for :user ,
:controllers => {
:sessions => 'user/sessions'
}
So, it now redirects to your views in app directory not to devise.
Then, you can do whatever you need.
You can add this line <%= devise_error_messages! %> in your form to get error messages..
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<%= f.label :login, "Login:", class: "login-label" %>
###
### Other codes
<% end %>
If you need to render other than default view you can specify a layout for those action.
I ended up figuring it out. (minus the AJAX notifications part)
my updated, overriden session controller:
class SessionsController < Devise::SessionsController
before_action :authenticate_user!, :only => [:control_panel]
def new
super
end
def create
super
end
def destroy
super
end
def control_panel
# Control panel page
end
end
I also put my login form partial in the views/sessions directory, and removed the original devise sessions_controller.rb and the associated new.html.erb view.
Removing the remote: true parameter from my logout link allowed the page to refresh upon logout, updating the buttons and such things.

Login and Sign up in the home page

I recently started working with Ruby on Rails and using the Devise gem for authentications. Worked fine for me, when I started a new app to try the Devise Gem. However, the template I'm using has both the Log in and Sign up forms on the Home page. Since Devise create two new views, I won't be needing them, and I will have to move these two views code to my home.html.erb file.
The issue I'm having is, after following these steps, the form is created, and I fill in the username and password, but pressing the submit button does absolutely nothing. Furthermore, there are three links created under the submit bottom; login, signup, and forgot password, that redirect me to the pages that Devise creates, the same pages I am trying to avoid.
I've looked in many places, but with no luck. I have a one week experience with RoR and two days experience with the Devise Gem, so I must be doing something ridiculously dumb. Any help?
Here is the sign-in form that I copied over to my home.html.erb file
<%= form_for("user", :url => user_session_path) do |f| %>
<div><%= f.label :email %><br />
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<% if devise_mapping.rememberable? -%>
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
<% end -%>
<div><%= f.submit "Sign in" %></div>
<% end %>
<%= render "devise/shared/links" %>
And here is my application helper file:
module ApplicationHelper
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
end
and my config/routes.rb file:
devise_for :users
root 'pages#home'
EDIT suggested routes.rb
get "pages/home"
devise_for :users
devise_scope :users do
get "pages/home", :to => "devise/sessions#new"
post "/users/sign_in", :to => "devise/sessions#create", :as => :sign_in
end
Still, no result.
Not sure how your routes are setup but if you want to display a custom sign-in/sign-up view and have it direct you to the appropriate place you'll need to make sure you change the scope of the devise routes and direct them to your custom views/routes.
For example, if you wanted to change your post route for signing up (after you hit submit) you might have something like this:
devise_scope :user do
get "/home", :to => "devise/sessions#new", :as => :home
post "/users/sign_up", :to => "devise/sessions#create", :as => :sign_up
end
I just made up the name spacing for your purposes but it might be different.
It would be worth while to take a look at the Devise documentation which describes in more detail how to change default sign-in and sign-up routes here
Hope this helps.
EDIT:
After looking at this further, though the information above will be helpful to you, I think what actually might be your problem is that you are missing :user on your first line of the form_for. Try doing this instead:
<%= form_for(:user, :url => user_session_path(:user)) do |f| %>
This is how its shown at the link I provided above.

overriding redirect_to behavior with devise update method

What I thought would be a simple problem is giving me some trouble. I have an application with Devise/Omniauth for authentication. When a new user signs up and logs in, I want them to be directed to a couple of questions (in succession) asking:
what beverages do they like? (coffee, drink, both)
what neighborhoods are convenient for them?
My user model has a "preferred_beverage" attribute along with three preferred_neighborhood_# attributes (preferred_neighborhood_one, preferred_neighborhood_two, preferred_neighborhood_three).
When a user signs in, I want to check if they are a new user and if so, direct them to a page with question 1:
<div class="center hero-unit">
<h2>What do you like to drink?</h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
<%= f.radio_button :preferred_beverage, 'Coffee', :checked => true %>
<%= label :preferred_beverage, 'Coffee' %>
<%= f.radio_button :preferred_beverage, 'Drink' %>
<%= label :preferred_beverage, 'Drink' %>
<%= f.radio_button :preferred_beverage, 'Both' %>
<%= label :preferred_beverage, 'Both' %>
<%= f.submit "Next", class: "btn btn-large btn-primary" %>
<% end %>
</div>
The problem is that, upon submitting this form, even though the user's "preferred_beverage" attribute is saving, I cannot properly overwrite the default Devise "update" method, which seems to be handling any modifications to User and redirecting to the root_path. I want to redirect straight to my /app/views/neighborhood_survey/new.html.erb form upon submission of the 'preferred_beverage'.
What exactly do I need to put in the "update" section of this registrations_controller.rb that I am using to override the default Devise registrations_controller?
app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def create
super
session[:omniauth] = nil unless #user.new_record?
end
def update
end
private
def build_resource(*args)
super
if session[:omniauth]
#user.apply_omniauth(session[:omniauth])
#user.valid?
end
end
end
I completed the below to ensure that my registrations_controller was overwriting the Devise registrations controller with the following:
routes.rb
devise_for :users, path_names: {sign_in: "login", sign_out: "logout"},
controllers: {omniauth_callbacks: "omniauth_callbacks", :registrations => "registrations"}
Any input is appreciated!
Thank you.
You should not be overriding devise update action, you need to be overriding the
after_sign_in_path_for
route of devise. You will be able to redirect the user wherever you like after he signs in. You should put this in your application controller.
http://rubydoc.info/github/plataformatec/devise/master/Devise/Controllers/Helpers#after_sign_in_path_for-instance_method

Devise confirmable, how to resend a confirmation email on click?

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" %>

Resources