Reputation: 1984
I have two types of admin users
Using cancan for following things.
Super admin can create Institution admin / another super admin as well as normal users related to the institution, and can manage all the other things like interest types, goals....etc. Also super admin can see all the users created in the system.
Institution admin can create user only related to the institution and can see only users related to that institution.
So everything working fine unless 1 thing. When i logged in with institutional admin and go on the page to create a new user it shows me following error.
ActiveRecord::HasManyThroughNestedAssociationsAreReadonly in Admin::UsersController#new
Cannot modify association 'AdminUser#users' because it goes through more than one other association.
models/admin_user.rb
class AdminUser < ActiveRecord::Base
belongs_to :institution
has_many :profiles, :through => :institution
has_many :users, :through => :profiles
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
def password_required?
new_record? ? false : super
end
def all_users
if role == "super_admin"
User.unscoped
else
#may be below line of code has issue.
users
end
end
end
models/ability.rb
class Ability
include CanCan::Ability
def initialize(current_admin_user)
# Define abilities for the passed in user here. For example:
current_admin_user ||= AdminUser.new # guest user (not logged in)
case current_admin_user.role
when "super_admin"
can :manage, :all
when "institution_admin"
can :manage, User, :profile => {:institution_id => current_admin_user.institution_id}
can :manage, InterestType, :institution_id => current_admin_user.institution_id
end
end
end
controllers/users_controller.rb
class UsersController < ApplicationController
layout "home"
skip_before_filter :require_login, :only => [:new, :create, :activate]
def new
@user = User.new
@user.build_profile
end
def create
@user = User.new(params[:user])
if @user.save
redirect_to home_path, :notice => "Please check email."
else
render :new, :alert => @user.errors
end
end
end
admin/admin_users.rb
ActiveAdmin.register AdminUser do
menu :if => proc{ can?(:manage, AdminUser) } controller.authorize_resource
index do
column :email
column ("Role") {|admin_user| admin_user.role == "super_admin" ? "Administrator" : "Institution Administrator" }
column ("Instituion") { |admin_user| admin_user.institution.name unless admin_user.institution_id.nil? }
column :current_sign_in_at
column :last_sign_in_at
column :sign_in_count
default_actions end
#... form do |f|
f.inputs "Admin Details" do
f.input :email
f.input :role, :as => :select, :include_blank => false, :collection => Hash[ "Institution Administrator", "institution_admin", "Administrator", "super_admin"]
f.input :institution_id, :as => :select, :include_blank => false, :collection => Institution.all.map{ |ins| [ins.name, ins.id] }
end
f.buttons end
after_create { |admin| admin.send_reset_password_instructions unless admin.email.blank? and admin.institution_id.blank? }
def password_required?
new_record? ? false : super end
end
admin/users.rb
ActiveAdmin.register User do
menu :if => proc{ can?(:manage, User) }
controller.authorize_resource
scope_to :current_admin_user, :association_method => :all_users
index do
column :username
column :email
column ("Instituion") { |user| user.profile.institution.name }
column :activation_state
column("Name" ) {|user| user.profile.users_firstname + " " + user.profile.users_lastname}
column :created_at
end
form do |f|
f.inputs "User Details" do
f.input :username
f.input :email
if f.object.id.nil?
f.input :password
f.input :password_confirmation
end
end
f.inputs "Profile Details", :for => [:profile, f.object.profile || Profile.new] do |profile_form|
profile_form.input :users_firstname
profile_form.input :users_lastname
profile_form.input :users_telephone
profile_form.input :class_year, :collection => 1995..2020, :selected => Time.now.year
if current_admin_user.role == "super_admin"
profile_form.input :institution_id, :as => :select, :include_blank => false, :collection => Institution.all.map{ |ins| [ins.name, ins.id] }
elsif current_admin_user.role == "institution_admin"
profile_form.input :institution_id, :as => :hidden, :value => current_admin_user.institution_id
end
end
f.buttons
end
end
Note: When i edit def all_users in admin_users.rb from users to User.scoped i can create new user from institution user but on index page i can see all the users(instead of only the users from the institution)
Upvotes: 2
Views: 1525
Reputation: 7344
You can fix the error by modifying the users
line in all_users
to:
User.joins(:profile => :institution).where(["#{Institution.table_name}.id = ?", institution.id])
This wont set the profile on the new user, so you will still need to assign that in the form the same way you assign institution. You will also need to make sure the bi-directional associations exist between Profile and Institution.
The reason you got the error is because ActiveRecord will not allow you to create new records off of a nested has_many through association. We are are working around this by getting the results of the same association but without going through a has_many. The query is the equivalent of saying "get me all users who are part of a profile, which is in turn part of an institution, where the institution's id is the same as the current user's institution's id".
Upvotes: 1