I have a simple setup with Cancan and Devise.
I wanna show the edit button only to "Admins" and Users which owns the "location":
So in the show.html.erb I have
<% if can? :manage, location, :user_id == current_user.id %>
<%= link_to 'Edit', edit_location_path %>
<% end %>
My ability.rb looks like this
if user.roles.include?('User')
can [:show, :index, :create], Location
can :manage, Location, :user_id => user.id
end
The locationController contains this on the very top:
load_and_authorize_resource
User Id is referenced in Location.user_id
But it does not show up the EDIT Button...
Did I miss something?
PS: user.id does not work in the view. and current_user.id does not work in the ability.rb
PPS: My roles are stored in an array eg: Roles ["User", "Admin"] and works well.
I've got it working with
can :manage, Location, :user_id => user.id.to_s
I hope it helps someone!
Related
I installed Devise and Cancan. My user has role super_admin with options shown below:
def initialize(user)
user ||= User.new # guest user
can :manage, :all
end
all works well except deleting elements. When I'm trying to delete object:
<%= link_to 'Destroy', place, :method => :delete, :data => { :confirm => 'Are you sure?' } %>
it keeps logging me out and redirecting to sign_in page
My class looks like that:
class PlacesController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource
Ok, I figured it out.
My layout file did not have authenticity_token tag
<%= csrf_meta_tag %>
so POST and DELETE requests couldn't have been authenticated.
I have tried to get this working, looked at multiple tutorials, questions on here tried different things for about a week now and I can't get the view to work correctly.
I have teams of users. A team has_many users and a user belongs_to a team (one team at a time). I know the association works because I got it working using the console (with some help there). I'm not sure how to get it working in the view. Below is the code, please let me know if more is needed.
What am I missing?
_join_team_button
<%= form_for(#user) do |f| %>
<%= f.submit "Join Team", class: "btn btn-large btn-primary" %>
<% end %>
Team Show Page
<%= render 'shared/join_team_button %>
Teams Controller
def show
#team = Team.find(params[:id])
#team_members = #team.users
#user = current_user.users.build if signed_in?
end
Users Controller
def show
#user = User.find(params[:id])
#teams = #user.team
end
I tried to put a complete demonstration of what you are looking for. Let me know if it fits for you.
#FILE: models/team.rb
class Team < AR::Base
has_many :users
end
#FILE: models/user.rb
class User < AR::Base
belongs_to :team
end
#FILE: config/routes.rb
#Here you are defining "users" as a nested resource of "teams"
resources :teams do
resources :users do
member do
put :join
end
end
end
#if you run "rake routes" it will show you the following line along with others
join_team_user PUT /teams/:team_id/users/:id/join(.:format) users#join
#FILE: controllers/team_controller.rb
def show
#team = Team.find(params[:id])
#team_members = #team.users
#user = current_user.users.build if signed_in?
end
#FILE: views/teams/show.html.erb
<% if(#user) %>
<%= form_for #user, :url => join_team_user_path(#team, #user) do |f| %>
<%= f.submit "Join Team", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
#You dont really need a form for this. You can simply use `link_to` like below
<%= link_to 'Join', join_team_user_path(#team, #user), method: :put %>
#FILE: controllers/users_controller.rb
def join
# params[:id] => is the user_id
#user = User.find(params[:id])
# params[:team_id] => is the team_id
#team = Team.find(params[:team_id])
# Now make the relationship between user and team here.
#user.update_attribute(:team, #team)
end
Update:Based on your comment
Q: Do I create a new user's resource and nest that or do I nest the already establishes user's resource?
Ans: Based on your requirements any resource can be defined both independently or nestedly. But yes you can control that which method will be available in which way. Like in your case, you can allow only join method when your user is nested under team resource.
resources :users, :only=>:join do
member do
put :join
end
end
resource :users
run rake routes with and without :only=>:join option and see differences in available routes.
Q: Will that affect other things?
Ans: If you strictly define your routes following above example, it should not affect other things. You should confirm all the available routes to your application by rake routes.
Q: Should I put my current routes.rb file up there?
Ans: Assuming your current routes.rb will be modified in the above way. Could I answer the question?
Q: Confused about the comments controller?
Ans: Im extreamely sorry. Yes it must be users_controller.rb as the rake routes command is showing. Result of copy and paste from my own example code :P
Q: what should I put there? the build method
Ans: In your case both the user and team is already exists in your database. All you need to do is just setup a relationship. So you can just use update_attribute option. Ive changed the join method. Please check. But yes if want to create new entries you might need build methods.
Sorry for the late reply :)
I figured it out. Still not perfect, but it gets the association working. The team, and the user were already created, I just needed to establish the association, so the build method would not have worked. Here's what I have:
View:
<%= form_for(#user) do |f| %>
<%= f.hidden_field :team_id, :value => #team.id %>
<%= f.submit "Join Team", class: "btn btn-large btn-primary" %>
<% end %>
Teams Controller:
def show
#team = Team.find(params[:id])
#team_members = #team.users
#user = User.find(params[:id]) if signed_in?
end
I have an app (a tutorial) which has Articles and Comments. An Article has_many Comments. A Comment belongs_to an Article. I'm having a problem deleting an Article's Comment. Here are the files in question:
app/views/comments/_comment.html.erb
<%= div_for comment do %>
<h3>
<%= comment.name %> <<%= comment.email %>> said:
<span class='actions'>
<%= link_to 'Delete', [#article, comment], confirm: 'Are you sure?', method: :delete %>
</span>
</h3>
<%= comment.body %>
<% end %>
CommentsController
before_filter :load_article
def create
#comment = #article.comments.new(params[:comment])
if #comment.save
redirect_to #article, :notice => 'Thanks for your comment'
else
redirect_to #article, :alert => 'Unable to add comment'
end
end
def destroy
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to #article, :notice => 'Comment deleted'
end
private
def load_article
#article = Article.find(params[:article_id])
end
routes.rb
resources :articles do
resources :comments
end
The problem is when I'm at address localhost:3000/articles/1 and try to delete a comment. Instead of being redirected to the Article show action I get this error at address localhost:3000/articles/1/comments/3:
Unknown action
The action 'show' could not be found for CommentsController
any help greatly appreciated,
thanks,
mike
You have two basic options here, because a link in most browsers can only send a GET request.
First option is to include the java-script default files into the page
<%= javascript_include_tag :defaults %> #this mocks a delete action by modifying the request automatically
Second and much preferable is to use button_to instead. First, there is a logical separation between a link to a place and a button to do something. Delete is definitely an action. Further, buttons aren't followed by spiders so nothing ever gets accidentally called.
<%= button_to 'delete', #comment, :method => :delete %>
========= EDIT FOR COMPLETENESS =======
If you are worried about the links and buttons not looking the same, an easy solution is to us jquery/jquery_ui to style all links and buttons exactly the same.
I have a standard devise installation, and I'm trying to add in functionality to add a user with a gym membership from an admin panel.
routes.rb
devise_for :users
resources :users
I am creating the user from the gym controller, so this is my gym action
def members
#gym = Gym.find(params[:id])
#user = User.new
#user.gym_users.build
#roles = Role.all
end
The gym_user is accepted in the user model
accepts_nested_attributes_for :gym_users
Then here is a portion of my form
<%= form_for #user do |f| %>
<%= f.label :email %><br />
<%= f.text_field :email %>
<% f.fields_for :gym_users do |builder| %>
<%= builder.label :item_id, "Membership Level" %><br />
<%= builder.collection_select(:item_id, #gym.membership_items, :id, :name, {:include_blank => true}) %>
<% end %>
<% end %>
What I'm having trouble with is my user controller where I actually create the user. This is the route for the membership page where the user is created
match 'gyms/:id/members' => 'gyms#members'
Finally, here's the create method on my users_controller
def create
#user = User.new(params[:user])
if #user.save
:notice => "User created successfully"
render :new
else
render :new
end
end
What I'm not sure is how to send back to that url when there is an error so that my model errors go with it, or redirect when it completes correctly.
I resolved this by making it an ajax call which eliminated the need for a redirect.
i don't quite understand how to restrict access to links in this particular case with CanCan. I always get "Edit" link displayed.
So i believe the problem is in my incorrect definition of cancan methods(load_ and authorize_).
I have CommentsController like that:
class CommentsController < ApplicationController
before_filter :authenticate_user!
load_resource :instance_name => :commentable
authorize_resource :article
def index
#commentable = find_commentable #loading our generic object
end
......
private
def find_commentable
params.each { |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.includes(:comments => :karma).find(value)
end }
end
end
and i have in comments/index.html.erb following code that render file from other controller:
<%= render :file => "#{get_commentable_partial_name(#commentable)}/show.html.erb", :collection => #commentable %>
you can think about "#{get_commentable_partial_name(#commentable)}" like just "articles" in this case.
Content of "articles/show.html.erb":
<% if can? :update, #commentable %>
<%= link_to 'Edit', edit_article_path(#commentable) %> |
<% end %>
my ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.role? :admin
can :manage, :all
elsif user.role? :author
can :read, [Article, Comment, Profile]
can :update, Article, :user_id => user.id
end
end
end
i have tried debug this issue like that
user = User.first
article = Article.first
ability = Ability.new(user)
ability.can?(:update, article)
and i always get "=> true" in ability check
Note: user.role == author and article.user_id != user.id
if you need more information please write
thank's for your time && sorry for my english
okay i figure it out, redetermined rules in ability.rb so now order is like guest->author->moderator->admin and problem is solved. I believe root of problem was in cancan logic which assumes that i need to redefine rules or do it in order i've show before