My users are set up through Devise. I also can use CanCanCan.
I set up an articles model and any user can create articles. They can only delete and edit their own article creations. On the index, they can view all articles that have been created by all users. There is currently an option to View, Edit, and Delete. I only want that option visible on the articles that are owned by the user. I want all other article lines to be blank. (Except for admin of course.) Users can view posts on views/articles/index.html.erb
<table>
<tr>
<th>Title</th>
<th>Description</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.description %></td>
<td><%= link_to 'View', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Delete', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
How can I allow users to only see the Edit and Delete button on the post that they own?
I tried this but it does not work:
<table>
<tr>
<th>Title</th>
<th>Description</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.description %></td>
<td><%= link_to 'View', article_path(article) %></td>
<% if user_signed_in? && current_user.articles.exists?(@article.id) %>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Delete', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
<% end %>
</table>
I've also tried:
<% if current_user && current_user.articles.exists?(@article.id) %>
Here is what my articles controller looks like: (I know I need to make it look better.)
def create
@article = current_user.articles.build(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
def update
@article = Article.find(params[:id])
if user_signed_in? && current_user.articles.exists?(@article.id)
if @article.update(article_params)
redirect_to @article
else
render 'edit'
end
elsif current_user && current_user.admin_role?
if @article.update(article_params)
redirect_to @article
else
render 'edit'
end
else
redirect_to @article
end
end
def destroy
@article = Article.find(params[:id])
if user_signed_in? && current_user.articles.exists?(@article.id)
@article.destroy
redirect_to articles_path
elsif current_user && current_user.admin_role?
@article.destroy
redirect_to articles_path
else
redirect_to articles_path
end
end
As you have access to the current_user
helper provided by Devise, then you can compare it with the article's owner. This can be in the view, to render the proper links to perform the actions:
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.description %></td>
<td><%= link_to 'View', article_path(article) %></td>
<% if current_user == article.user %>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Delete', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
<% end %>
You can move this verification to a helper, in the ApplicationHelper to be available in most of your views, like:
module ApplicationHelper
def owner?(object)
current_user == object.user
end
end
You pass the object, and this returns true or false depending if the current user is equal to the object
user. The view only changes to:
<% if owner?(article) %>
If you want to move this verification to the controller, or also if you want to use it in both cases, then you can do the same verification in the controller. As the owner?
helper method isn't available in the controller, you can just redirect back in case the current_user isn't the article's owner, like:
def edit
unless current_user == @article.user
redirect_back fallback_location: root_path, notice: 'User is not owner'
end
end
If you want to move this part to a before
callback, to be able to use it in the edit
and destroy
method, then you can add it as a private method, and having access to the @article
you can do such comparison, it'd be:
private
def owner?
unless current_user == @article.user
redirect_back fallback_location: root_path, notice: 'User is not owner'
end
end
This way you just need to add it as a before_action
callback:
before_action :owner?, only: %i[edit destroy]
Most probably before the one that defines the @article variable.
Note the use of redirect_back
in Rails 5, previous versions might use redirect_to :back
.