Reputation: 871
I'm a little new to it, but I'm building a new web app using rails. Most of what I've got so far is based on railstutorial.org. I've only got a few possible user "roles" (basic user, excom, and admin), so I'm just modeling it using a couple boolean fields in the user model.
I'd like my admin users to be able to make other users admin or excom, without having to resort to some full blown user role modeling system.
I don't want admins to be able to modify other user data (like name, email, etc.) or of course allow users to make themselves admin, so adding something like that to the users_controller update method seems cumbersome and error prone. But it also seems like a whole new controller and routes is overkill.
I just want a button for admins to click to "Make user admin" and have it work, but I'm not sure of the "right" way to implement that.
Edit:
The only exposure an admin has at this point, is checking whether a user is an admin in some before_action. I.e.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
or
def correct_user_or_excom_or_admin
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user) || current_user.admin? || current_user.excom?
end
I think what I want is how to define a route such that I can write the following method in the users_controller and include it in the admin_user before_action.
def make_admin
@user = User.find(params[:id])
@user.admin = true
@user.save
flash[:success] = "#{@user.name} is now an Admin"
end
And then be able to include the following in the appropriate view
<%= link_to "Make Admin", user_admin_path(user), method: :post,
data: { confirm: "You sure?" } %>
I think @widjajayd answer is on the right track. Does creating custom routes that way include the user id in the params?
Upvotes: 0
Views: 390
Reputation: 871
Here's the solution I came up with, with inspiration taken from @widjalayd.
Create the following custom routes.
post '/users/:id/make_admin', to: 'users#make_admin', as: :make_admin
delete '/users/:id/remove_admin', to: 'users#remove_admin', as: :remove_admin
post '/users/:id/make_excom', to: 'users#make_excom', as: :make_excom
delete '/users/:id/remove_excom', to: 'users#remove_excom', as: :remove_excom
Create the corresponding methods in the users_controller being sure they are in the admin_user before_action
def make_admin
@user = User.find(params[:id])
@user.admin = true
@user.save
flash[:success] = "#{@user.name} is now an Admin"
redirect_to users_url
end
def remove_admin
@user = User.find(params[:id])
@user.admin = false
@user.save
flash[:success] = "#{@user.name} is no longer an Admin"
redirect_to users_url
end
def make_excom
@user = User.find(params[:id])
@user.excom = true
@user.save
flash[:success] = "#{@user.name} is now an Executive Committee Member"
redirect_to users_url
end
def remove_excom
@user = User.find(params[:id])
@user.excom = false
@user.save
flash[:success] = "#{@user.name} is no longer an Executive Committee Member"
redirect_to users_url
end
And the partial for displaying a user on the index page is then
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
|
<%= link_to "Delete", user, method: :delete,
data: { confirm: "You sure?" } %>
|
<% if user.admin? %>
<%= link_to "Remove Admin", remove_admin_path(user), method: :delete,
data: { confirm: "You sure?" } %>
<% else %>
<%= link_to "Make Admin", make_admin_path(user), method: :post,
data: { confirm: "You sure?" } %>
<% end %>
|
<% if user.excom? %>
<%= link_to "Remove Excom", remove_excom_path(user), method: :delete,
data: { confirm: "You sure?" } %>
<% else %>
<%= link_to "Make Excom", make_excom_path(user), method: :post,
data: { confirm: "You sure?" } %>
<% end %>
<% end %>
</li>
And then write some tests to be sure.
test "admins should be able to make and remove new admins" do
log_in_as(@user)
post make_admin_path(@other_user)
assert @other_user.reload.admin?
delete remove_admin_path(@other_user)
assert_not @other_user.reload.admin?
end
test "non admins can't make or remove admins" do
log_in_as(@other_user)
delete remove_admin_path(@user)
assert @user.reload.admin?
post make_admin_path(@another_user)
assert_not @another_user.reload.admin?
end
test "admins should be able to make and remove executive committee" do
log_in_as(@user)
post make_excom_path(@another_user)
assert @another_user.reload.excom?
delete remove_excom_path(@another_user)
assert_not @another_user.reload.excom?
end
test "non admins can't make or remove executive committee" do
log_in_as(@another_user)
post make_excom_path(@user)
assert_not @user.reload.excom?
delete remove_excom_path(@other_user)
assert @other_user.reload.excom?
end
Edit:
This probably is pushing the limit of "good/maintainable" code and the "rails-way", which is why I asked the question. But since this works, and took a lot less time than learning and setting up a full blown roles system like devise I'll stick with it for now. If I need to make any significant changes then I will probably switch to devise.
Upvotes: 0
Reputation: 6253
you can create custom route with custom method for admin
inside routes.rb, create 2 routes for new and create just for admin
resources users do
collection {
get :new_admin
put :create_admin
}
end
inside user_controllers.rb, create 2 methods
def new_admin
@user = User.new
# this depending with what system you use devise/bcryt/others
end
def create_admin
@user = User.new(user_params)
@user.role = "Admin"
# this depending with what system you use devise/bcryt/others
end
create view file inside app/users/new_admin.html.erb
<%= form_for @user, url: create_admin_users_path, do |f| %>
# your fields name, password, etc
<% end %>
button availability just for admin user
<% if user.role == admin %>
<%= link_to 'Make User Admin', new_admin_users_path, :class => 'form-control btn btn-info' %>
<% end %>
below usually you list user in index.html.erb
<% if @users.any? %>
<table id="table-user" class="table table-striped">
<thead>
<tr>
<th>email</th>
<th>name</th>
<th>Role</th>
<th class="edit"></th>
<th class="destroy"></th>
</tr>
</thead>
<tbody>
<tr>
<% @user.each do |user| %>
<td><%= user.email %></td>
<td><%= user.username %></td>
<td><%= user.role %></td>
<td><%= link_to "Make Admin", create_admin_users_path(user_id: user.id), method: :post,
data: { confirm: "You sure?" } %> </td>
<% end %>
</tbody>
</table>
<% end %>
from form you pass params with hash user_id (it can be any name you want) then inside create controller you get the params with sample below
def create_admin
@user = User.find(params[:user_id])
@user.admin = true
@user.save
flash[:success] = "#{@user.name} is now an Admin"
end
Upvotes: 0