How would one render a page say a "/logout" page that displays a "Thanks for visiting us" then if the user reloads the browser it would load "/" instead?
I have a
match "/logout" => "home#logout"
But don't wan't anyone that requesting "/logout" will see this page, it should only be rendered direct after the user is signed_out.
What would be the best way to approach this render a view dependant with a conditional redirect ( to root_path) instead of using a redirect_to
You probably want:
match '/logout' => 'sessions#destroy', :via => :delete
And use logout_path in your link_to helper or however you decide to implement logging out in your application.
And write your message in the flash in SessionsController#destroy. It may look something like:
class SessionsController < ApplicationController
def destroy
sign_out # or whatever you named your method for signing out
flash[:notice] = "Thanks for visiting us"
redirect_to root_path
end
end
In order to make sure the request goes to root_path when the user is not signed in, you should place a before_filter in ApplicationController:
class ApplicationController < ActionController::Base
before_filter :authenticate_user
def authenticate_user
unless signed_in?
redirect_to root_path
end
end
helper_method :authenticate_user
end
This way, after the user is signed out, all requests will redirect to root_path.
To allow requests to pages without being signed in, use skip_before_filter in the appropriate controller classes:
def MyPublicStuffsController < ApplicationController
skip_before_filter :authenticate_user
# ...
end
Related
I have a before_filter called check_login that looks something like this:
def check_login
if not session[:user_id]
flash[:error] = "Please log in to continue"
redirect_to login_path
end
end
I then put this before_filter in my application controller, and then exclude it in my login controller (with skip_before_filter :check_login)
The problem is that when the user hits the homepage for the first time (i.e. just localhost:3000), it will redirect them to the login page with the flash[:error] message displaying. However, for the homepage, I just want to show the login form. What's the cleanest way to handle this 'special-case'? I thought about putting the skip_before_filter in the controller that handles the homepage, but I didn't think this was very DRY, since if I change the homepage in the routes file, I'll have to also change the location of the skip_before_filter.
Thanks!
You can add some action in your filter
class LoginController < ApplicationController
skip_before_filter :check_login, :only => [:login]
def login
end
end
And in Application Controller, "blank?" check on presence and nil. It useful
def check_login
if session[:user_id].blank?
flash[:error] = "Please log in to continue"
redirect_to login_path
end
end
You can add named action for your homepage:
class StaticPagesController < ApplicationController
def home
end
end
And then check the current action in your callback:
def check_login
if not session[:user_id]
flash[:error] = "Please log in to continue" unless params[:action] == "home"
redirect_to login_path
end
end
I have a controller PostsController, which allows user to create posts before logging in. But to save it, user has to log in using Omniauth.
In PostsController, I have:
class PostsController < ApplicationController
before_filter :authenticate_user_by_service, :only => :create
def create
...
end
private
def authenticate_user_by_service
redirect_to user_omniauth_authorize_path(:facebook)
end
Now, I have another controller to handle callback from facebook, which is called ServicesController
class ServicesController < ApplicationController
def create
auth = request.env["omniauth.auth"]
... authentication logic here ...
sign_in(:user, service.user)
end
method_alias: :facebook, :create
Normally, for authentication, after sign in, I'll redirect to :back.
However, services#create here is used as a before_filter. In this case, what should I do to get it back to my Posts#create ?
Update: I got this warning saying the filter chain is interrupted right at the moment I refer to a different method
Started POST "/posts" for 127.0.0.1 at 2013-02-26 23:47:41 -0500
Processing by PostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"H0as8=", "post"=>{"post"=>"bla bla"}, "commit"=>"Create Post"}
Redirected to http://localhost:3000/users/auth/facebook
Filter chain halted as :authenticate_user_by_service rendered or redirected
You're approaching this wrongly. You're handling login and the check wether someone is logged in in the same step.
Consider using a sessions_controller to handle all your signup/login/logout logic, for example:
class SessionsController < ApplicationController
def new # this will be /login
session[:return_to] = params[:returnto] unless params[:returnto].nil?
redirect_to "/auth/facebook"
end
def create # this will be the callback after the user is authenticated
auth_token = request.env["omniauth.auth"]["credentials"]["token"]
# you'll need to write this based on your app's requirement.
# Find a user or create one if he doesn't exist yet.
user = User.find_or_create_authenticated_user(auth_token)
if user.present?
session[:user_id] = user.id # this stores the current user's id in your session and lets Rails remember him for you.
redirect_to return_or(products_url) # see below for the usage of return_or
return
end
redirect_to root_url, alert: 'User not found or invalid'
end
def destroy # /logout
session[:user_id] = nil
redirect_to root_url
end
end
#routes.rb
match '/logout' => 'sessions#destroy', :as => :logout
match '/login' => 'sessions#new', :as => :login
match '/auth/facebook/callback' => 'sessions#create'
Then, in your ApplicationController you set up a couple of helper methods:
class ApplicationController < ActionController::Base
protected
# Use this in your views and controllers to get
# a handle of the user that is currently logged in.
# it will return nil if there is no user logged in.
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
# Use this to check wether a user is logged in.
# returns true if there is a user, false otherwise.
def logged_in?
!current_user.nil?
end
helper_method :logged_in?
# Block access to controllers or actions based on
# wether there's a user or not.
def require_login
unless logged_in?
# if we need the user to log in, we send him on his way
# but send him back to the current page afterwards.
session[:return_to] = request.fullpath
redirect_to root_url(subdomain: false), :alert => "Please login"
end
end
# When a user is not logged in, but you send him to log in,
# you can force him to return to the url he was in or if nothing
# was set go to a standard path.
# See this being set up in SessionsController#new and in require_login and then
# being used in SessionsController#create
def return_or(path)
session.delete(:return_to) || path
end
helper_method :return_or
end
These helper methods are available in all your controllers since they all inherit from ApplicationController. You could then tell your PostsController to send users who are not logged in to go and log in and after doing that they get returned to the PostsController.
Then to address your requirement of saving a post only after authentication: Either you do create the post, save it, but only update it to being public after the user is authenticated, or you store the contents of the post in the session and restore them after the user is authenticated:
class PostsController < ApplicationController
def new
#post = Post.new(session[:post_params] || {})
end
def create
if logged_in?
#post = Post.create(params[:post])
# ... etc
else
session[:post_params] = params[:post]
session[:return_to] = new_post_path
end
end
end
Note that this is a rather vulnerable approach. I would rather suggest to actually create the Post, mark it as not yet public and store only the post's id in the Session. After authentication you could then find that post_id, recreate the object from, set its status to public and associate it with the current_user.
I'm using devise and trying the next following:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :is_worker
def is_worker
if user_signed_in?
#email = current_user.email
if #email && Worker.find_by_email(#email).nil?
redirect_to '/tasksadmins'
else
redirect_to '/workers'
end
else
redirect_to '/users/sign_in'
end
end
end
when I try to enter the site: localhost:3000/tasksadmins, I got:
Oops! It was not possible to show this website
The website at http://localhost:3000/tasksadmins seems to be unavailable. The precise error was:
Too many redirects
It could be temporarily switched off or moved to a new address. Don't forget to check that your internet connection is working correctly.
How can I fix it please?
before_filter is applied to every single request. That's why it's redirecting again and again.
You might want to only filter specific actions:
before_filter :is_worker, only: :index
Another solution would be to check wether a redirect is necessary in #is_worker:
redirect_to '/workers' unless request.fullpath == '/workers'
EDIT:
Another way would be to skip the before filter for the target actions of your redirects. Example:
class WorkersController < ApplicationController
skip_before_filter :is_worker, only: :index
# …
end
In my case:
users_controller.rb
before_action :logged_in?, only: :new
def new
#user = User.new
render layout: "session"
end
and
application_controller.rb
def logged_in?
redirect_to users_new_url unless current_user.present?
end
When I was trying to redirect to the 'users/new' page,same error occurred.
This is just because I'm trying to redirect to the 'users/new' page and "def logged_in?" is also redirecting to the same page.
Then I changed the application_controller.rb code like this:
def logged_in?
redirect_to root_url unless current_user.blank?
end
Error_Resolved.
Devise is behaving strangely. It shows the red or the error messages, but it does not show the green or success messages.
This happened after I made changes to the routing and controller to redirect to sign-in page or home page according to whether the user is signed-in or not.
So the routes.rb now has:
devise_for :users
root :to => "questions#redirect_on_visit"
match 'home', :to => "questions#index"
and the controller:
before_filter :authenticate_user!, :except => [:redirect_on_visit]
def redirect_on_visit
if user_signed_in?
redirect_to home_path
else
redirect_to new_user_session_path
end
end
Also changed application controller to redirect to appropriate pages after sign-in/sign-out:
protected
def after_sign_in_path_for(resource)
stored_location_for(:user) || root_path
end
private
def after_sign_out_path_for(resource)
stored_location_for(:user) || root_path
end
I was being extremely numb. The fact that I have root :to => "questions#redirect_on_visit", will redirect each time a user visits, signs in or signs out. Redirect was clearly washing away the flash messages.
Finally I found the solution I was looking for. It is to preserve the flash messages flash.keep(:notice) as mentioned here.
Your problem is the multiple redirection from login => root => home
as flash object has a life leave only for a single request
I guess It would be better of writing redirect_on_visit routing in after_sign_in_path_for and after_sign_out_path_for
something like this
def after_sign_in_path_for(resource)
stored_location_for(:user) || home_path
end
private
def after_sign_out_path_for(resource)
stored_location_for(:user) || new_user_session_path
end
a user of my website must first login to see every page of it. My current solution results in a "too many redirects" error message in the browser, I suspect something goes wrong so it endlessly goes back and forth, but where?
In the application controller I have this:
before_filter :authenticate_user
def authenticate_user
unless session[:user_id]
flash[:alert] = I18n.t :session_unauthenticated
redirect_to login_path
end
end
The "login_path" goes to "sessions/new", which looks like this:
def new
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_url, :notice => I18n.t(:session_logged_in)
else
flash.now.alert = I18n.t(:session_invalid)
render "new"
end
end
Thanks for your help!
Your before_filter is active for each and every action (since it's in the ApplicationController) and therefore also for your sessions#new action. Meaning you cannot even enter the login action. I suggest you put the before_filter in every needed controller and specify the actions with before_filter :authenticate_user, :except => [:new, :create, :destroy] or whatever you need.