I added an admin boolean column to my user table and toggled my user to yes. I changed the following code so that the "edit" button shows to teh admin for all pins:
<% if current_user == pin.user || current_user.admin? %>
<p>
<%= link_to 'Edit', edit_pin_path(pin) %>
<%= link_to content_tag(:i, "", class:"icon-trash"), pin, method: :delete, data: { confirm: 'Are you sure?' } %>
</p>
<% end %>
When I try to edit a pin, I get an error: Couldn't find Pin with id=37-outer [WHERE "pins"."user_id" = 1]
I believe this is because of the pins controller, but I haven't figured out how to correctly change it to allow admins to edit. Here's my controller:
def edit
#pin = current_user.pins.find(params[:id])
end
I have a feeling this is an easy fix that I'm just missing.
you are scoping pins to the current_user which is the reason why you're getting the error. Try changing the code that finds the pin to
klass = Pin
klass = klass.where(user_id: current_user.id) unless current_user.admin?
#pin = klass.find(params[:id])
alternatively, if this is something you'll be using in a lot of places, it may be good to define a class method instead
# app/models/pin.rb
def self.find_for_user(pin_id, user)
user.admin? ? find(pin_id) : user.pins.find(pin_id)
end
# in your controller
#pin = Pin.find_for_user(params[:id], current_user)
Related
Im trying to add votes to comments on my blog application. But i cant seem to be able to send the variable within the below loop to the controller:
<% #comments.each do |c| %>
<h1><%= c.title %></h1>
<p><%= c.content %></p>
<p><%= c.user.username %></p>
<% if current_user.id == c.user_id %>
<%= link_to "Edit", edit_comment_path(#post, c)%>
<%= link_to "Delete", delete_comment_path(#post, c), method: :delete, data: { confirm: "Are you sure?" }%>
<% end %>
<% if current_user.id != c.user_id %>
<p>Review this comment</p>
<%= link_to like_path(c), method: :put do %>
Upvote
<%= c.upvote %>
<% end %>
<%= link_to dislike_path(c), method: :put do %>
Downvote
<%= c.downvote %>
<% end %>
<% end %>
<% end %>
controller:
...
def upvote
object_comment = Comment.find(#comment.id)
object_comment.increment!(:upvote)
redirect_to show_path(#post)
end
def downvote
object_comment = Comment.find(#comment.id)
object_comment.increment!(:downvote)
redirect_to show_path(#post)
end
....
Routes:
Rails.application.routes.draw do
....
#Comments
post '/post/:post_id', to: 'comment#create', as: 'new_comments'
get '/post/:post_id/:comment_id/edit', to: 'comment#edit', as: 'edit_comment'
put '/post/:post_id/comment/:comment_id', to: 'comment#update', as: 'update_comment'
delete '/post/:id/:comment_id/', to: 'comment#destroy', as: 'delete_comment'
put 'like', to: "comment#upvote", as: 'like'
put 'dislike', to: "comment#downvote", as: 'dislike'
end
I would like to receive the 'c' stated on the each loop as a variable on the upvote and downvote methods to replace the #comment within object_comment = Comment.find(#comment.id), in order to increment the votes. is this possible?
As it is right i obviously receive the following error:
NoMethodError (undefined method `id' for nil:NilClass):
Your *_path methods do not know what to do with that single argument. (If you want to pass an object to your links, you might want to research RESTful routing. However, to solve it with your current routes, I suggest
<%= link_to "Downvote #{c.downvote}" dislike_path(id: c.id) %>
It should then be possible to access this id attribute in the controller via params.
def dislike
#comment = Comment.find(params[:id])
...
end
A couple of other things to think about:
It's very unusual to perform a put action on clicking a link. You might want to consider doing this as a get action instead.
Does the downvote method return something which is different for each comment? If not, it's standard practise to include it in the controller's helper.
You can get id in parameters, please check below methods
def upvote
object_comment = Comment.find(params[:id])
object_comment.increment!(:upvote)
redirect_to show_path(#post)
end
def downvote
object_comment = Comment.find(params[:id])
object_comment.increment!(:downvote)
redirect_to show_path(#post)
end
....
I think you're doing it wrong. You'll need to save the user's action on the comment with a model CommentVote for example that belongs to user and comment.
Then you'll need to do something like:
class CommentVote
belongs_to :comment
belongs_to :user
enum type: [:upvote, :downvote]
def self.vote(type:, comment_id:, user_id:)
comment_vote = CommentVote.where(comment_id: comment_id, user_id: user_id).first_or_initialize
comment_vote.type = type
comment_vote.save
end
end
And then do a after_save :update_counts in the CommentVote model:
def update_counts
self.comment.upvote = self.comment.comment_votes.upvote.count
self.comment.downvote = self.comment.comment_votes.downvote.count
self.comment.save
end
and you'll call it like: CommentVote.vote(type: :upvote, comment_id: 1, user_id: 1)
So I'm obviously confused with boolean flow because I run into a problem each time. NOTE: I am teaching myself to program, you are all my only hope in learning! I am trying to define a method that checks if a user is an admin, so that I can display certain objects in views to ONLY admins simple enough...or not, for some reason it's not recognizing that I'm an admin (when I truly am, I've checked!). With this all being said, I am a newb so go easy on me!
helpers/sessions_helper is used by both my User and Blogpost models:
def current_user #determines if user is logged out or is the current user of the session/cookie of profile
if (user_id = session[:user_id])
#current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(cookies[:remember_token])
log_in user
#current_user = user
end
end
end
def current_user?(user)
user == current_user
end
def is_an_admin
if current_user && current_user.admin?
end
end
<div class="col-xs-12">
<h1><%= #blogpost.title %></h1>
<p><%= #blogpost.content %></p>
<% if is_an_admin %>
<%= link_to "Return to blog", blogposts_path %>
<%= link_to "Edit Post", edit_blogpost_path, class: 'btn btn-primary' %> |
<%= link_to "Delete Post", blogpost_path(#blogpost),
method: :delete, data: {confirm: "Are you sure?"}, class: 'btn btn-danger' %>
<% else %>
<%= link_to "Return to blog", blogposts_path %>
<% end %>
</div>
I'm unsure if maybe I have the method in the wrong place? I have tried placing it inside my applications controller and my user controller to no avail. I must be missing something.
Your sintax got messed up, but I think you're always returning nil on the is_an_admin method:
def is_an_admin
if current_user && current_user.admin?
end
end
It does nothing if the condition is true so it's always returning nil
Change it to something like:
def is_an_admin?
current_user and current_user.admin? # or shorter: current_user.try('admin?')
end
I am using devise gem. My project's purpose user can upload files & delete thier own file but not other user's file. So, I write "user?" method for ensure that correct user can only show the delete button. I also ensure the 'correct_user' method for delete file. but now I'm faceing this problem "NoMethodError at /upload_files.."
"undefined method `user?' "
Here is my upload_files_controller.rb file:
class UploadFilesController < ApplicationController
before_action :logged_in
before_action :correct_user, only: :destroy
def index
#upload_files = UploadFile.all
end
def new
#upload_file = UploadFile.new
end
def create
#upload_file = current_user.upload_files.build(upload_params)
if #upload_file.save
redirect_to upload_files_path, notice: "The file #{#upload_file.name} has been uploaded."
else
render "new"
end
end
def destroy
upload_file = UploadFile.find(params[:id]).destroy
redirect_to upload_files_path, notice: "The file #{upload_file.name} has been deleted."
end
def user?(check_user)
check_user == current_user.id
end
private
def upload_params
params.require(:upload_file).permit(:name, :upload_file)
end
def logged_in
if admin_signed_in?
return true
else
authenticate_user!
end
end
def correct_user
#upload_file = current_user.upload_files.find_by(id: params[:id])
redirect_to root_url if #upload_file.nil?
end
end
Here is my upload_files/index.html.erb file:
<% #upload_files.each do |file| %>
<tr>
<td><%= file.name %></td>
<td><%= link_to "Download File", file.file_name_url %></td>
<%if admin_signed_in? %>
<td><%= button_to "Delete", file, method: :delete, class: "btn btn-danger", confirm: "Are you sure that you wish to delete #{file.name}?" %></td>
<% else user?(file.user_id) %>
<td><%= button_to "Delete", file, method: :delete, class: "btn btn-danger", confirm: "Are you sure that you wish to delete #{file.name}?" %></td>
<% end %>
</tr>
<% end %>
what did I wrong? please show me a way.
Thanks,
Mezbah
You should place your user? method in helper.
About your view code, why don't you use boolean alternative?
<% if admin_signed_in? || user?(file.user_id) %>
<td><%= button_to "Delete", file, method: :delete, class: "btn btn-danger", confirm: "Are you sure that you wish to delete #{file.name}?" %></td>
<% end %>
Marek is right, however, you may wish to use an authorization gem such as CanCanCan for this
There's a great Railscast about authorization here:
To give you a brief synopsis, authorization is the authority that a user has to CRUD an object. Authentication (Devise) is for giving the user "permission" to use various features in the application; authorization is allowing the user to edit / change data depending on their level of access
Your choice of trying to add a button so that users will be able to remove their own object. This is perfect CanCanCan territory:
--
CanCanCan
This gem was originally called "CanCan", but as Ryan Bates has gone on leave, some of the Rails community took it upon themselves to make their own gem, calling it CanCanCan
The way it works is relatively simple:
Have an "ability" model to define user abilities
Call the can? method to determine if a user can partake in a particular action
This means that you'll be able to make an extensible piece of functionality which will grant your users access to specific objects as required. Here's how:
> rails g cancan:ability
This will create the ability model to define all the methods:
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :read, :all
end
end
end
This will give you the ability to then call the can? method on your various objects:
<td>
<% if admin_signed_in? || (can? :destroy, file) %>
<%= button_to "Delete", file, method: :delete, class: "btn btn-danger", confirm: "Are you sure that you wish to delete #{file.name}?" %>
<% end %>
</td>
I would add the following method to your application_controller.rb. That makes that method available in all controllers and views within your application.
# application_controller.rb
def current_user?(user)
current_user == user
end
helper_method :current_user?
Use that method like this in your view:
# in view
<% if admin_signed_in? || current_user?(file.user) %>
<td>
<%= button_to('Delete', file,
method: :delete,
class: 'btn btn-danger',
confirm: "Are you sure that you wish to delete #{file.name}?") %>
</td>
<% end %>
I am going through Michael Hartl's book, "Ruby on Rails Tutorial Learn Web Development with Rails". In the section where he explains how to get administrative access to delete users, I can't seem to get this to work. The delete link won't show up on the web app, and I am mystified as to why this is occurring. This is my unfactored code below, which sits in the directory of: app/views/users/index.html.erb
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>
<ul class="users">
<% #users.each do |user| %>
<li>
<%= gravatar_for user, size: 52 %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "Are you sure?" } %>
<% end %>
</li>
<% end %>
</ul>
<%= will_paginate %>
The methods of current_user.admin? && !current_user?(user) are methods located in: app/helpers/sessions_helper.rb
def current_user
remember_token = User.encrypt(cookies[:remember_token])
#current_user ||= User.find_by(remember_token: remember_token)
end
def current_user?(user)
user = current_user
end
I've been stuck on this problem for quite a while and have tried many different things to solve the problem, and none of them have worked. I'm running Rails on Windows 7 using the Rubymine IDE. The tutorial was done on a Mac OS X operating system, and I'm thinking maybe the problem might be associated with my setup.
Also are there known issues with tests failing using the Windows cmd when running RSpec versus running it on Mac OS X?
Any help would be much appreciated.
Thank you.
I just figured out what was wrong!
This method was written incorrectly
def current_user?(user)
user = current_user
end
The correct method to get the corresponding code above to work is this:
def current_user?(user)
user == current_user
end
Thank you for all your input!
Make sure in your controller you're calling the destroy action in your controller. Your code looks fine thus far and I'm assuming you're not using cancan for authorization.
In view file
<% if current_user.admin? && !current_user?(user) %>
<%= link_to "delete", user, method: :delete, data: { confirm: "Are you sure?" } %>
<% end %>
Controller
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_path }
format.json { head :no_content }
end
end
I am trying to setup so that users will get a "not authorized" message if they click edit for a profile that is not theirs. This message should of course not appear for admins since admins can edit all profiles. I previously done this on Permission.rb, however I got rid of the file to go with a more basic user roles/authorization.
I don't see how I can implement what I had previously on Permission.rb for my current files. I have tried some solutions but they don't add up. If someone could point me in the right direction that will be great. Also I am doing this all from scratch, user authentication/authorization.
index.html.erb:
<% #users.each do |user| %>
<li>
<% if current_user.admin? || current_user == #user %>
<% end %>
<%= link_to "Edit #{user} profile", user %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?"} %>
</li>
<% end %>
Why are you giving the chance for users to edit other peoples profiles?
First, you should have a if statement in your view where you show the link for the edit page. I guess this is showing up on a profile of every user, so i suppose the code in your controller is something like this:
def show
#user = User.find(params[:id])
end
Then in your view you should have something like this:
<% if current_user.admin? || current_user == #user %>
<%= link_to 'Edit Profile' , edit_user_path(#user) %>
<% end %>
There is also a case if someone tries to 'force' their way in, just like trying to type a url www.yourapplication.com/users/6/edit you could write a before_filter method in your controller:
before_filter :check_privileges, only => [:edit, :update]
and then write a method in called check_privileges
def check_privileges
unless current_user.admin? || current_user.id == params[:id]
flash[:warning] = 'not authorized!'
redirect_to root_path
end
end
EDIT: After the questioner edited his code, i'm showing the mistake:
You are putting the end too soon:
<% #users.each do |user| %>
<li>
<%= link_to user.name, user %>
<% if current_user.admin? || current_user == #user %>
<%= link_to "Edit #{user} profile", user %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?"} %>
<% end %>
</li>
<% end %>