I have comments underneath a bunch of questions, right now the code I have shows the delete comment link under everyone's comments not just the user who made the comment. How do I fix this so that users can only delete comments they made themselves? I am using devise gem to authenticate users.
<% commentable.comments.each do |comment| %>
<h6 style="text-align:left; margin-bottom: 0px;"><strong><%= comment.user.profile.first_name %> <%= comment.user.profile.last_name %>: </strong></h6>
<p style="text-align:left">
<%= comment.body %>
</p>
<% if current_user %>
<p style="text-align:left; font-size: 12px; margin-top: -10px"><%= link_to 'Delete', [comment.user, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %></p>
<% end %>
<% end %>
comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
#comment = #commentable.comments.new(comment_params)
#comment.user = current_user
if #comment.save
redirect_back(fallback_location: root_path)
end
end
def update
#comment.update(comment_params)
end
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
redirect_back(fallback_location: root_path)
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
comment.rb
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
belongs_to :user
end
user.rb
class User < ApplicationRecord
before_create :add_role_to_user
ROLES = %w[admin member].freeze
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
has_many :comments, dependent: :destroy
def add_role_to_user
self.role = 'member'
end
end
Change your view:
<% if current_user && current_user == comment.user %>
Change your controller:
def destroy
# ensure user only can find owner comment.
#comment = current_user.comments.find(params[:id])
#comment.destroy
redirect_back(fallback_location: root_path)
end
If you change <% if current_user %> in your view to <% if current_user && current_user == comment.user %> then the Delete link will only appear for the owner of that comment.
You should also check that the current_user matches the #comment.user in your destroy controller method.
Related
I'm trying to setup a simple rails app with job board functionality. I was able to add jobs to the database, until I added an association between my Job model and devise User model. Now it won't update the database when I fill out the form.
jobs_controller
class JobsController < ApplicationController
def index
#jobs = Job.all
end
def new
#job = Job.new
end
def listing
end
def listings
end
def create
#job = Job.new(params.require(:job).permit(:title, :description, :url, :user_id))
if #job.save
redirect_to root_path
else
render "new"
end
end
end
new.html.erb
<%= simple_form_for #job do |form| %>
<%= form.input :title, label: "Job title" %>
<%= form.input :description, label: "Description" %>
<%= form.input :url, label: "URL" %>
<%= form.button :submit %>
<% end %>
index.html.erb
<% #jobs.each do |job| %>
<div class="job">
<h2><%= link_to job.title, job.url %></h2>
<p><%= job.description %></p>
</div>
<% end %>
<p><%= link_to "Add a job", new_job_path %></p>
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :jobs
end
job.rb
class Job < ApplicationRecord
belongs_to :user
end
There isn't an error in the console, but the database doesn't seem to be updated or it's not updating the view.
I also ran a migration:
class AddUserToJob < ActiveRecord::Migration[5.2]
def change
add_reference :jobs, :user, foreign_key: true
end
end
You can get the user with current_user in Devise.
class JobsController < ApplicationController
# This restricts the actions to authenticated users and prevents a nil error
before_action :authenticate_user, except: [:show, :index]
# ...
def create
# this sets the user_id column
#job = current_user.jobs.new(job_params)
if #job.save
# you really should set a flash message or something to notify the user
# and possibly redirect to the show or index action instead
redirect_to root_path
else
render "new"
end
end
private
def job_params
params.require(:job)
.permit(:title, :description, :url, :user_id)
end
end
If you don't want to associate the job immediately to a user, you need to change the association to be optional, like:
class Job < ApplicationRecord
belongs_to :user, optional: true
end
Else you need to supply user_id in your form or set it in the controller action.
You should also delegate this part to a separate method
def job_params
params.require(:job).permit(:title, :description, :url, :user_id)
end
Job.new(job_params)
I'm making my first solo rails project. It's a facebook clone, and at the moment I can't get a user to accept a friend request. Please let me know if the information posted isn't sufficient enough to help work towards an answer.
friend_request_controller.rb
class FriendRequestsController < ApplicationController
before_action :set_friend_request, except: [:index, :create]
def index
#incoming = FriendRequest.where(friend: current_user)
#outgoing = current_user.friend_requests
end
def create
#user = User.find(current_user)
friend = User.find(params[:id])
#friend_request = current_user.friend_requests.new(friend_id: friend)
if #friend_request.save
redirect_to user_path(current_user)
end
end
def update
friend = User.find(params[:id])
#friend_request = current_user.friend_requests.find(friend_id: friend)
#friend_request.accept
end
show.html.erb
Hello my my email is <%= #user.email %>
<% if user_signed_in? %>
<li>
<%= link_to 'Logout', destroy_user_session_path, :method => :delete %></li>
<li><%= link_to 'All Users', users_path %>
</li>
<% else %>
<li>
<%= link_to('Login', new_user_session_path) %>
</li>
<% end %>
<ul>
<% current_user.friend_requests.each do |request| %>
<h4>You have new friend requests from:</h4>
<li>
<%= User.find(request.friend_id).email %>
<%= link_to "Accept", friend_request_path(friend_id: #friend),method:"put" %>
<%= link_to "Decline", "#" %>
</li>
<% end %>
</ul>
I know something is wrong with my link_to helper here
user.rb
class User < ApplicationRecord
has_many :friend_requests, dependent: :destroy
has_many :pending_friends, through: :friend_requests, source: :friend
has_many :friendships, dependent: :destroy
has_many :friends, through: :friendships
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
friend_request.rb
class FriendRequest < ApplicationRecord
belongs_to :user
belongs_to :friend, class_name: 'User'
def accept
user.friends << friend
destroy
end
def destroy
end
end
Do what #araratan has mentioned + in your frined_request controller, replace params[:id] with params[:friend_id].
Instead of:
<%= link_to "Accept", friend_request_path(friend_id: #friend),method:"put" %>
Try to change #friend to request.friend_id:
<%= link_to "Accept", friend_request_path(friend_id: request.friend_id),method:"put" %>
I am using devise as my user authentication and carrierwave gem for image uploading. Now everything works well and the avatar gets saved in the users table and is shown inside the index view; but not inside the show view.
To make my question a bit more clear:
Inside the index view, the avatar get's successfully shown.
Inside the show view, the avatar falls to the default image because #user.avatar is blank/nil
Show code:
<div class="well">
<div class="media">
<a class="pull-left">
<% if #user.avatar.blank? %>
<img src="http://www.adtechnology.co.uk/images/UGM-default-user.png" style="width: 75px;">
<% elsif #user.avatar %>
<%= image_tag #user.avatar, :style => "width:75px;" %>
<% end %>
</a>
<div class="media-body">
<p>About <%= link_to #question.user.username, #question.user, :class => " bg" %></p>
</div>
<p class="text-muted small">Apparently this user doesn't like to share his information.</p>
</div>
</div>
Question controller:
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#questions = Question.all
respond_with(#questions)
end
def show
#user = User.find(params[:id])
respond_with(#question)
end
def new
if user_signed_in?
#question = current_user.questions.build
respond_with(#question)
else
redirect_to new_user_session_path
end
end
def edit
end
def create
#question = current_user.questions.build(question_params)
#question.save
respond_with(#question)
end
def update
#question.update(question_params)
respond_with(#question)
end
def destroy
#question.destroy
respond_with(#question)
end
private
def set_question
#question = Question.find(params[:id])
end
def question_params
params.require(:question).permit(:title, :description)
end
end
User model:
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
has_many :questions, :dependent => :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Question model:
class Question < ActiveRecord::Base
belongs_to :user
end
I fixed it by changing these lines inside the show.html.erb :
<% if #question.user.avatar.blank? %>
<img src="http://www.adtechnology.co.uk/images/UGM-default-user.png" style="width: 75px;">
<% elsif #question.user.avatar %>
<%= image_tag #question.user.avatar, :style => "width:75px;" %>
<% end %>
def show
#user = User.find(params[:id])
respond_with(#question)
end
Since the show action's called in QuestionsController, params[:id] will be #question's id. You should use #question.user to refer to the author of #question:
def show
#user = #question.user
respond_with(#question)
end
name
metric
date_value
result_value
None of the above attributes are showing when I load the index page.
I know its because of this line: <% if averaged.user == current_user %>
More specifically it's because of the nested attributes of date_value and result_value (because if I take out those nested attributes the name & metric will show)
What do I need to add to the controller to allow the nested attributes to show on the index page, and in effect all the attributes?
I am using the cocoon gem and the devise gem.
Thanks in advance for your service!
<div id="values" class="panel panel-default">
<div class="panel-heading"><h4><b>AVERAGE</b></h4></div>
<!-- Table -->
<table>
<% #averaged_quantifieds.each do |averaged| %>
<% if averaged.user == current_user %>
<th class="value">
<%= link_to edit_quantified_path(averaged) do %>
<%= averaged.name %>
<% end %>
(<%= averaged.metric %>)
</th>
<tbody class="value">
<td><%= averaged.date_value.strftime("%m-%Y") %></td>
<td><%= averaged.result_value %></td>
</tbody>
<% end %>
<% end %>
</table>
</div>
controller
class QuantifiedsController < ApplicationController
before_action :set_quantified, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#averaged_quantifieds = current_user.quantifieds.averaged
#instance_quantifieds = current_user.quantifieds.instance
#averaged_quantifieds = Result.all.order("date_value")
#instance_quantifieds = Result.all.order("date_value")
end
def show
end
def new
#quantified = current_user.quantifieds.build
#quantified = Quantified.new
end
def edit
end
def create
#quantified = current_user.quantifieds.build(quantified_params)
if #quantified.save
redirect_to quantifieds_url, notice: 'Quantified was successfully created'
else
render action: 'new'
end
end
def update
if #quantified.update(quantified_params)
redirect_to quantifieds_url, notice: 'Goal was successfully updated'
else
render action: 'edit'
end
end
def destroy
#quantified.destroy
redirect_to quantifieds_url
end
private
def set_quantified
#quantified = Quantified.find(params[:id])
end
def correct_user
#quantified = current_user.quantifieds.find_by(id: params[:id])
redirect_to quantifieds_path, notice: "Not authorized to edit this goal" if #quantified.nil?
end
def quantified_params
params.require(:quantified).permit(:categories, :name, :metric, :result, :date, results_attributes: [:id, :result_value, :date_value, :_destroy])
end
end
quantified.rb
class Quantified < ActiveRecord::Base
belongs_to :user
scope :averaged, -> { where(categories: 'averaged') }
scope :instance, -> { where(categories: 'instance') }
has_many :results
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true
CATEGORIES = ['averaged', 'instance']
end
result.rb
class Result < ActiveRecord::Base
belongs_to :quantified
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :goals
has_many :values
has_many :quantifieds
has_many :results
end
Please let me know if you need any more code or comments to help bring this question to a resolution.
undefined method avatar?' for nil:NilClass
undefined methodname' for nil:NilClass
Hi, I'm receiving the following errors in my partial. The reason I listed both is because after commenting out the line causing the first error message, I get the second error which leads me to believe the problem isn't with "avatar" or "name" specifically, but with something else,though I don't know what. In rails console, I'm able to call user and name on a comment. I also seeded the database using Faker if that matters. Here's the partial.
<%= content_tag :div, class: 'media', id: "comment-#{comment.id}" do %>
<%= link_to '#', class: 'pull-left' do %>
<%= image_tag(comment.user.avatar.small.url) if comment.user.avatar? %>
<% end %>
<div class="media-body">
<small>
<%= comment.user.name %> commented <%= time_ago_in_words(comment.created_at) %> ago
<% if policy(comment).destroy? %>
| <%= link_to "Delete", [#topic, #post, comment], method: :delete %>
<% end %>
</small>
<p><%= comment.body %></p>
</div>
<% end %>
Also, please see the render.
<div class="col-md-4">
<% if policy(Comment.new).create? %>
<h4>Leave a comment</h4>
<br/>
<%= render partial: 'comments/comment', locals: { topic: #topic, post: #post, comment: #comment } %>
<% end %>
</div>
The below are my user model and comments_controller
class UsersController < ApplicationController
before_filter :authenticate_user!
def update
if current_user.update_attributes(user_params)
flash[:notice] = "User information updated"
redirect_to edit_user_registration_path(current_user)
else
render "devise/registrations/edit"
end
end
private
def user_params
params.require(:user).permit(:name, :avatar)
end
end
Comments_controller
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.find(params[:post_id])
#comments = #post.comments
#comment = current_user.comments.build(comment_params)
#comment.post = #post
#new_comment = Comment.new
authorize #comment
if #comment.save
redirect_to [#topic, #post], notice: "Comment was submitted successfully."
else
flash[:error] = "There was an error submitting the comment. Please try again."
end
end
I've already reset the database, but to no avail. Stuck as to what the issue is. Thanks for your help.
Please see below for my User and Comment models.
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
default_scope { order('created_at DESC') }
validates :body, length: { minimum: 5 }, presence: true
after_create :send_favorite_emails
private
def send_favorite_emails
self.post.favorites.each do |favorite|
if favorite.user_id != self.user_id && favorite.user.email_favorites?
FavoriteMailer.new_comment(favorite.user, self.post, self).deliver
end
end
end
end
User model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :posts
has_many :comments
has_many :votes, dependent: :destroy
has_many :favorites, dependent: :destroy
mount_uploader :avatar, AvatarUploader
def role?(base_role)
role == base_role.to_s
end
def favorited(post)
self.favorites.where(post_id: post.id).first
end
def voted(post)
self.votes.where(post_id: post.id).first
end
private
end
If you're getting
undefined method foo for nil:NilClass
it's that the thing you're calling your method on is nil.
So in your case, you're calling avatar? and name on something nil.
Looking at your code, it's clear comment.user is (a) what those methods are called on, and hence (b) what is nil.
Result: your comment has no user. Either enforce all comments (including new/empty/stub ones) to have an user (blank user?), or make your view so that a user is not necessary.
The issue was discovered. In the partial render
comment: #comment
should be
comment: comment