lib/rails_admin/config.rb
# frozen_string_literal: true
require 'rails_admin/config/lazy_model'
require 'rails_admin/config/sections/list'
require 'active_support/core_ext/module/attribute_accessors'
module RailsAdmin
module Config
# RailsAdmin is setup to try and authenticate with warden
# If warden is found, then it will try to authenticate
#
# This is valid for custom warden setups, and also devise
# If you're using the admin setup for devise, you should set RailsAdmin to use the admin
#
# @see RailsAdmin::Config.authenticate_with
# @see RailsAdmin::Config.authorize_with
DEFAULT_AUTHENTICATION = proc {}
DEFAULT_AUTHORIZE = proc {}
DEFAULT_AUDIT = proc {}
DEFAULT_CURRENT_USER = proc {}
class << self
# Application title, can be an array of two elements
attr_accessor :main_app_name
# Configuration option to specify which models you want to exclude.
attr_accessor :excluded_models
# Configuration option to specify a allowlist of models you want to RailsAdmin to work with.
# The excluded_models list applies against the allowlist as well and further reduces the models
# RailsAdmin will use.
# If included_models is left empty ([]), then RailsAdmin will automatically use all the models
# in your application (less any excluded_models you may have specified).
attr_accessor :included_models
# Fields to be hidden in show, create and update views
attr_reader :default_hidden_fields
# Default items per page value used if a model level option has not
# been configured
attr_accessor :default_items_per_page
# Default association limit
attr_accessor :default_associated_collection_limit
attr_reader :default_search_operator
# Configuration option to specify which method names will be searched for
# to be used as a label for object records. This defaults to [:name, :title]
attr_accessor :label_methods
# hide blank fields in show view if true
attr_accessor :compact_show_view
# Tell browsers whether to use the native HTML5 validations (novalidate form option).
attr_accessor :browser_validations
# set parent controller
attr_reader :parent_controller
# set settings for `protect_from_forgery` method
# By default, it raises exception upon invalid CSRF tokens
attr_accessor :forgery_protection_settings
# Stores model configuration objects in a hash identified by model's class
# name.
#
# @see RailsAdmin.config
attr_reader :registry
# Bootstrap CSS classes used for Navigation bar
attr_accessor :navbar_css_classes
# show Gravatar in Navigation bar
attr_accessor :show_gravatar
# accepts a hash of static links to be shown below the main navigation
attr_accessor :navigation_static_links
attr_accessor :navigation_static_label
# Set where RailsAdmin fetches JS/CSS from, defaults to :sprockets
attr_writer :asset_source
# Setup authentication to be run as a before filter
# This is run inside the controller instance so you can setup any authentication you need to
#
# By default, the authentication will run via warden if available
# and will run the default.
#
# If you use devise, this will authenticate the same as _authenticate_user!_
#
# @example Devise admin
# RailsAdmin.config do |config|
# config.authenticate_with do
# authenticate_admin!
# end
# end
#
# @example Custom Warden
# RailsAdmin.config do |config|
# config.authenticate_with do
# warden.authenticate! scope: :paranoid
# end
# end
#
# @see RailsAdmin::Config::DEFAULT_AUTHENTICATION
def authenticate_with(&blk)
@authenticate = blk if blk
@authenticate || DEFAULT_AUTHENTICATION
end
# Setup auditing/versioning provider that observe objects lifecycle
def audit_with(*args, &block)
extension = args.shift
if extension
klass = RailsAdmin::AUDITING_ADAPTERS[extension]
klass.setup if klass.respond_to? :setup
@audit = proc do
@auditing_adapter = klass.new(*([self] + args).compact, &block)
end
elsif block
@audit = block
end
@audit || DEFAULT_AUDIT
end
# Setup authorization to be run as a before filter
# This is run inside the controller instance so you can setup any authorization you need to.
#
# By default, there is no authorization.
#
# @example Custom
# RailsAdmin.config do |config|
# config.authorize_with do
# redirect_to root_path unless warden.user.is_admin?
# end
# end
#
# To use an authorization adapter, pass the name of the adapter. For example,
# to use with CanCanCan[https://github.com/CanCanCommunity/cancancan/], pass it like this.
#
# @example CanCanCan
# RailsAdmin.config do |config|
# config.authorize_with :cancancan
# end
#
# See the wiki[https://github.com/railsadminteam/rails_admin/wiki] for more on authorization.
#
# @see RailsAdmin::Config::DEFAULT_AUTHORIZE
def authorize_with(*args, &block)
extension = args.shift
if extension
klass = RailsAdmin::AUTHORIZATION_ADAPTERS[extension]
klass.setup if klass.respond_to? :setup
@authorize = proc do
@authorization_adapter = klass.new(*([self] + args).compact, &block)
end
elsif block
@authorize = block
end
@authorize || DEFAULT_AUTHORIZE
end
# Setup configuration using an extension-provided ConfigurationAdapter
#
# @example Custom configuration for role-based setup.
# RailsAdmin.config do |config|
# config.configure_with(:custom) do |config|
# config.models = ['User', 'Comment']
# config.roles = {
# 'Admin' => :all,
# 'User' => ['User']
# }
# end
# end
def configure_with(extension)
configuration = RailsAdmin::CONFIGURATION_ADAPTERS[extension].new
yield(configuration) if block_given?
end
# Setup a different method to determine the current user or admin logged in.
# This is run inside the controller instance and made available as a helper.
#
# By default, _request.env["warden"].user_ or _current_user_ will be used.
#
# @example Custom
# RailsAdmin.config do |config|
# config.current_user_method do
# current_admin
# end
# end
#
# @see RailsAdmin::Config::DEFAULT_CURRENT_USER
def current_user_method(&block)
@current_user = block if block
@current_user || DEFAULT_CURRENT_USER
end
def default_search_operator=(operator)
if %w[default like not_like starts_with ends_with is =].include? operator
@default_search_operator = operator
else
raise ArgumentError.new("Search operator '#{operator}' not supported")
end
end
# pool of all found model names from the whole application
def models_pool
(viable_models - excluded_models.collect(&:to_s)).uniq.sort
end
# Loads a model configuration instance from the registry or registers
# a new one if one is yet to be added.
#
# First argument can be an instance of requested model, its class object,
# its class name as a string or symbol or a RailsAdmin::AbstractModel
# instance.
#
# If a block is given it is evaluated in the context of configuration instance.
#
# Returns given model's configuration
#
# @see RailsAdmin::Config.registry
def model(entity, &block)
key =
case entity
when RailsAdmin::AbstractModel
entity.model.try(:name).try :to_sym
when Class, ConstLoadSuppressor::ConstProxy
entity.name.to_sym
when String, Symbol
entity.to_sym
else
entity.class.name.to_sym
end
@registry[key] ||= RailsAdmin::Config::LazyModel.new(key.to_s)
@registry[key].add_deferred_block(&block) if block
@registry[key]
end
def asset_source
@asset_source ||=
begin
detected = defined?(Sprockets) ? :sprockets : :invalid
unless ARGV.join(' ').include? 'rails_admin:install'
warn <<~MSG
[Warning] After upgrading RailsAdmin to 3.x you haven't set asset_source yet, using :#{detected} as the default.
To suppress this message, run 'rails rails_admin:install' to setup the asset delivery method suitable to you.
MSG
end
detected
end
end
def default_hidden_fields=(fields)
if fields.is_a?(Array)
@default_hidden_fields = {}
@default_hidden_fields[:edit] = fields
@default_hidden_fields[:show] = fields
else
@default_hidden_fields = fields
end
end
def parent_controller=(name)
@parent_controller = name
if defined?(RailsAdmin::ApplicationController) || defined?(RailsAdmin::MainController)
RailsAdmin::Config::ConstLoadSuppressor.allowing do
RailsAdmin.send(:remove_const, :ApplicationController)
RailsAdmin.send(:remove_const, :MainController)
load RailsAdmin::Engine.root.join('app/controllers/rails_admin/application_controller.rb')
load RailsAdmin::Engine.root.join('app/controllers/rails_admin/main_controller.rb')
end
end
end
def total_columns_width=(_)
ActiveSupport::Deprecation.warn('The total_columns_width configuration option is deprecated and has no effect.')
end
def sidescroll=(_)
ActiveSupport::Deprecation.warn('The sidescroll configuration option was removed, it is always enabled now.')
end
# Setup actions to be used.
def actions(&block)
return unless block
RailsAdmin::Config::Actions.reset
RailsAdmin::Config::Actions.instance_eval(&block)
end
# Returns all model configurations
#
# @see RailsAdmin::Config.registry
def models
RailsAdmin::AbstractModel.all.collect { |m| model(m) }
end
# Reset all configurations to defaults.
#
# @see RailsAdmin::Config.registry
def reset
@compact_show_view = true
@browser_validations = true
@authenticate = nil
@authorize = nil
@audit = nil
@current_user = nil
@default_hidden_fields = {}
@default_hidden_fields[:base] = [:_type]
@default_hidden_fields[:edit] = %i[id _id created_at created_on deleted_at updated_at updated_on deleted_on]
@default_hidden_fields[:show] = %i[id _id created_at created_on deleted_at updated_at updated_on deleted_on]
@default_items_per_page = 20
@default_associated_collection_limit = 100
@default_search_operator = 'default'
@excluded_models = []
@included_models = []
@label_methods = %i[name title]
@main_app_name = proc { [Rails.application.engine_name.titleize.chomp(' Application'), 'Admin'] }
@registry = {}
@navbar_css_classes = %w[navbar-dark bg-primary border-bottom]
@show_gravatar = true
@navigation_static_links = {}
@navigation_static_label = nil
@asset_source = nil
@parent_controller = '::ActionController::Base'
@forgery_protection_settings = {with: :exception}
RailsAdmin::Config::Actions.reset
RailsAdmin::AbstractModel.reset
end
# Reset a provided model's configuration.
#
# @see RailsAdmin::Config.registry
def reset_model(model)
key = model.is_a?(Class) ? model.name.to_sym : model.to_sym
@registry.delete(key)
end
# Perform reset, then load RailsAdmin initializer again
def reload!
reset
load RailsAdmin::Engine.config.initializer_path
end
# Get all models that are configured as visible sorted by their weight and label.
#
# @see RailsAdmin::Config::Hideable
def visible_models(bindings)
visible_models_with_bindings(bindings).sort do |a, b|
if (weight_order = a.weight <=> b.weight) == 0
a.label.casecmp(b.label)
else
weight_order
end
end
end
private
def viable_models
included_models.collect(&:to_s).presence || begin
@@system_models ||= # memoization for tests
([Rails.application] + Rails::Engine.subclasses.collect(&:instance)).flat_map do |app|
(app.paths['app/models'].to_a + app.config.eager_load_paths).collect do |load_path|
Dir.glob(app.root.join(load_path)).collect do |load_dir|
path_prefix = "#{app.root.join(load_dir)}/"
Dir.glob("#{load_dir}/**/*.rb").collect do |filename|
# app/models/module/class.rb => module/class.rb => module/class => Module::Class
filename.delete_prefix(path_prefix).chomp('.rb').camelize
end
end
end
end.flatten.reject { |m| m.starts_with?('Concerns::') } # rubocop:disable Style/MultilineBlockChain
@@system_models + @registry.keys.collect(&:to_s)
end
end
def visible_models_with_bindings(bindings)
models.collect { |m| m.with(bindings) }.select do |m|
m.visible? &&
RailsAdmin::Config::Actions.find(:index, bindings.merge(abstract_model: m.abstract_model)).try(:authorized?) &&
(!m.abstract_model.embedded? || m.abstract_model.cyclic?)
end
end
end
# Set default values for configuration options on load
reset
end
end