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.
Related
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
I'm wondering how can I print on the index of my project only the rooms with the :is_available column or the rooms table with the :true value (is boolean).
I can't figure out how to achieve this (Sorry but I'm new with Rails). Any advice will be very appreciate!
I've this error with my current code:
"ActiveRecord::RecordNotFound in RoomsController#home
Couldn't find Room without an ID"
Here is my rooms_controller code:
class RoomsController < ApplicationController
before_action :get_room, only: [:index, :home]
def index
end
def show
#room = Room.find(params[:id])
end
def home
if params[:set_locale]
redirect_to root_url(locale: params[:set_locale])
else
puts #rooms if Room.all(params[:is_available => :true])
end
end
def get_room
#rooms = Room.all
end
end
You already have got #rooms = Room.all, you just need to precise your query (from all to your is_available restriction).
def home
if params[:set_locale]
redirect_to root_url(locale: params[:set_locale])
else
puts #rooms.where(is_available: true)
end
end
Also, you should avoid using puts in your controller logic. Either pass variable to the view (you can change #rooms value or create new variable #available_rooms), respond_with it or log it using Rails.logger if you use puts as a debugging solution.
def index
end
def home
if params[:set_locale]
redirect_to root_url(locale: params[:set_locale])
elsif params[:is_available]
puts #rooms
end
end
def get_room
#rooms = Room.where(is_available: true)
end
Using puts in controller - not a good idea.Use view to show the data.
There are several issues you may have:
Routes
Your index method looks empty. I presume you're using "home" as a substitute
In this case, you have to know what type of action this is - a member or collection action? The reason this is important is that when you define your routes, you have to ensure you define the route in the right way. For your home route, I'd have done this:
#config/routes.rb
resources :rooms do
get "home", action: "home"
end
Scopes
You can use a scope to bring back all the values with :is_available present. This lives in the model like this:
#app/models/room.rb
Class Room < ActiveRecord::Base
scope :is_available?, -> { where(is_available: true) }
end
This will allow you to call
#room = Room.is_available?
Code
Although you've not given us any context of the error (when it happens, what you do to make it happen), this is what I would do to help fix it:
#app/controllers/rooms_controller.rb
def home
if params[:set_locale]
redirect_to root_url(locale: params[:set_locale])
else
puts Room.is_available?
end
end
This may change depending the params you send & how you send them
def home
if params[:set_locale]
redirect_to root_url(locale: params[:set_locale])
else
puts #rooms if params[:is_available] && Room.where(is_available: true)
end
end
should work.
In Rails3 application i have a number of models with user_id - this way i'm saying: it was created by some user.
Like:
current_user.id #=> 1
#item.user_id #=> 1
# this item created by user with id 1
And i want to restrict current_user's acess to items which was not created by him/her.
Something like:
if #item.user_id == current_user.id
#everything is fine
else
#redirect somwhere with flash "You don't have an access here"
end
What is the best way for this, because i have multiple number of models (and controllers to show/edit/destroy) with such a user_id?
Use CanCan!
With it you will be able to define permissions declaratively, like this:
can :read, Project, :user_id => user.id
And later enforce this rule:
def show
#project = Project.find(params[:id])
authorize! :read, #project
end
authorize! will raise an exception, but you can check in a more peaceful manner:
<%= link_to 'Link to a project', #project if can? :read, #project %>
You can intercept authorization errors and handle them in one place:
class ApplicationController < ActionController::Base
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_url, :alert => exception.message
end
end
The simplest way to do this, is to use Active Record's has_many.
Namely, in a controller, whenever you load the Item, you just say
#item = current_user.items.find(params[:id])
This way you don't have to do any work to check.
Rails 3.0.3
ruby 1.9.2p0
The Problem:
I have a Users table which has many items, the item(s) in turn therefore belongs to the Users.
In my model item.rb i attempt to save the item along with the value for the user.id so i have:
self.User_ID = #user.id
this however give me the error
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
this is causing some confusion that it can't find this as in the show.html.erb that 'wraps' this page <%= #user.id %> displays the correct ID on the page
Many thanks in advance
** EDIT **
The Shorten action is the action upon which i want to parameter to be passed
class ItemsController < ApplicationController
def redirect
#item = Item.find_by_shortened(params[:shortened])
if #item
#redirect_to #item.original
redirect_to #item.original
else
redirect_to :shorten
end
end
def shorten
#host = request.host_with_port
#user = current_user
You need to load the #user model in every action that will require access to it. Having it render properly in the show action will not guarantee it is loaded in the update action.
Usually you need to have something like this in your controller:
class UsersController < ApplicationController
before_filter :load_user, :except => [ :index, :new, :create ]
# ...
protected
def load_user
#user = User.find(params[:user_id] || params[:id])
rescue ActiveRecord::RecordNotFound
render(:text => 'Record not found')
end
end
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