I'm trying to get my head around this one:
Say you have two models where:
:bar has_many :foos
And you have a url like this: http://myapp.com/24-name-of-the-bar-to-param/foos/new
On my site this page shows a lot of information about the bar which users are going to create a foo for. So, even if a user isn't logged in the user will still be able to see the info.
Currently, when the user is logged in, a form to create a new foo is on the left hand side of the web page. When the user isn't logged in it says "Please login or register"
The form explains a lot about how my app works, so I'd like to change it so that even if a user isn't logged in the form will display, and if they click submit it will take them to the login_path and then when they login, back to the path where the submitted the form.
I'm running into this problem: Currently I have a login_required method in my application controller like this:
def store_location
session[:return_to] = request.request_uri
end
def login_required
unless current_user || admin?
store_location
flash[:notice] = "Please log in"
redirect_to login_path and return false
end
end
This login required action is called on the create action of the foo. When I click submit on the form it takes me to http://myapp.com/foos instead of http://myapp.com/24-name-of-the-bar-to-param/foos/new
I assume this is because the login required function is called on the create action and not the new action.
Any ideas?
UPDATE as per request here is the controller code and callbacks:
before_filter :find_bar, :except => [:index, :edit, :update]
before_filter :login_required, :only => [:create]
ssl_required :edit, :update
def new
#foo = Foo.new :amount => "0.00"
#foos = Foo.find(:all, :conditions => ["bar_id = ?", #bar.id], :order => "created_at DESC").paginate :page => params[:page], :per_page => 10
#foos_all = Foo.find(:all, :conditions => ["hatlink_id = ?", #hatlink.id], :order => "created_at DESC")
#current_user = current_user
#topfooers = User.bar_amount(#bar, nil)
#average_foo = #bar.foos.average('amount')
end
def create
#foo = #current_user.foos.build params[:foo]
if (#bar.foos << #foo)
flash[:notice] = "Thank you for fooing!"
redirect_to new_bar_foo_path(#bar)
else
render :action => :new
end
end
private
def find_bar
#bar_id = params[:bar_id]
return(redirect_to(categories_path)) unless #bar_id
#bar = Bar.find(#bar_id)
end
You could store the referring url (if it's present) & redirect to that page if the request was a POST or PUT. Something like:
def store_location
if request.post? || request.put?
session[:return_to] = request.env['HTTP_REFERER']
else
session[:return_to] = request.request_uri
end
end
Silly of me to come up with a solution just five minutes after posting the question. Oh well, here's what I did (and it works).
In the "new" action of foo I added these lines
if !current_user
store_location
end
In the login required method I have added this:
if params[:controller] == "foos" && params[:action] == "create"
#Took out the line for storing the location in this method.
flash[:notice] = "Please log in"
redirect_to login_path and return false
Related
I have a controller action that I need to call using AJAX. So I used skip_before_filter to turn off the requirement that I be logged in for that particular call.
That works, but on subsequent requests, I get redirected to the login form. How can I avoid this?
Here's the filter code in the controller:
protect_from_forgery
before_filter :logged_in?
skip_before_filter :logged_in?, :only => [:toggle_waiver]
Here's the controller action:
def toggle_waiver
#household = Household.find(params[:household_id])
#household.update_attributes(:waive_latefee => params[:bool])
render :nothing => true
end
The logged_in? method is simply a home grown one:
def self.authenticate(username, password)
user = find_by_username(username)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
Is there a better way to be doing this? (I'm using Rails 3... don't judge me) :-)
Here's the logged_in? controller method (from the application_controller):
protected
def logged_in?
unless session[:admin_id]
flash[:notice] = "Please log in."
redirect_to log_in_path
return false
else
return true
end
end
And the session is set by this method in the sessions controller:
def create
admin = Admin.authenticate(params[:username], params[:password])
if admin
session[:admin_id] = admin.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
You can make this much simpler by using the "except" clause of before_filter.
before_filter :logged_in, :except => [:toggle_waiver]
Also, "if a before filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter they are also cancelled." So ditch the true/false approach of your filter, and make it either render or redirect as the control of what happens.
I'm trying to make all a user's illicit attempts to see other users' show page redirect instead to their own show page.
None of my attempts in the else section of this code do the job though.
class UsersController < ApplicationController
def show
if params[:id] == current_user.id.to_s
liked_bookmark_ids = current_user.likes.pluck(:bookmark_id)
liked_bookmarks = Bookmark.where(id:liked_bookmark_ids)
liked_topic_ids = liked_bookmarks.pluck(:topic_id)
#liked_topics = Topic.where(id:liked_topic_ids).order('topics.name')
else
# redirect_to :controller => 'users', :id => current_user.id # gives a screwy url
# redirect_to user_path, :id => 6 # causes a redirect loop
# redirect_to :back # causes a redirect loop
end
end
end
What's the right way to go about it?
redirect_to user_path(current_user)
I think a better approach would be to use a filter to check if it's a request by a current user or not. You can do something like this:
class UsersController < ApplicationController
before_action :check_current_user, only: :show
def show
liked_bookmark_ids = current_user.likes.pluck(:bookmark_id)
liked_bookmarks = Bookmark.where(id:liked_bookmark_ids)
liked_topic_ids = liked_bookmarks.pluck(:topic_id)
#liked_topics = Topic.where(id:liked_topic_ids).order('topics.name')
end
private
def check_current_user
redirect_to current_user, notice: "Not authorized" if params[:id] != current_user.id.to_s
end
end
In my project controller I have the actions below. As you can see 'check_if_owner_or_member' is called when the 'show' view is rendered, which checks whether or not a user is a member of the project or administrator. If that's not the case the user get's an error message and gets redirected to the root.
When trying out the action it works if the user is admin, but not if the user is a member. So, something is apparently wrong with 'if !is_owner || !is_member', because it works if I only try with 'if !is_member'.
What am I doing wrong?
before_filter :check_if_owner_or_member, :only => [:show]
def is_owner
Project.where("id = ? AND user_id = ?", params[:id], current_user.id).count > 0
end
def is_member
ProjectsUser.where("project_id = ? AND user_id = ?", params[:id], current_user.id).count > 0
end
def check_if_owner_or_member
if !is_owner || !is_member
redirect_to root_path
flash[:error] = "You don't have permission to the project!"
end
end
You should refactor your code like this:
before_filter :check_if_owner_or_member, :only => [:show]
def is_owner?
Project.exists?(id: params[:id], user_id: current_user.id)
end
def is_member?
ProjectsUser.exists?(project_id: params[:id], user_id: current_user.id)
end
def check_if_owner_or_member
unless is_owner? || is_member? # as TheDude said, you probably meant && here
redirect_to root_path
flash[:error] = "You don't have permission to the project!"
end
end
It is more readable and using the exists? method which is faster to execute than finding, counting and comparing to zero.
Since a member is not an admin, the first part would be true and the second part won't be executed. I imagine that you would want to use && here.
This before_filter validates before update or create record if captcha is correct.
When it's incorrect, it takes me back to previous page but all of the input data will be gone....
How can I remain the input data that was typed in at previous page?
I'd like to use before_filter and apply these 2 actions 'update' and 'create'.
It should detect where the submit is come from and switches where to re-render 'new' or 'edit'
before_filter :simple_captcha_check, :only => [:update, :create]
def simple_captcha_check
if !simple_captcha_valid?
flash[:error] = 'Wrong Captcha!'
redirect_to :back
end
end
Assuming you're creating/updating an User model, your code can look like this:
def simple_captcha_check
if !simple_captcha_valid?
flash[:error] = 'Wrong Captcha!'
if request.put? # We came from an edit request
#user = User.find(params[:id])
#user.attributes = params[:user]
render :action => :edit
elsif request.post? # We came from a new request
#user = User.new params[:user]
render :action => :new
end
end
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.