activeadmin 2.2.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activeadmin might be problematic. Click here for more details.

Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -30
  3. data/CONTRIBUTING.md +50 -44
  4. data/README.md +9 -2
  5. data/app/assets/javascripts/active_admin/base.js +519 -0
  6. data/app/assets/stylesheets/active_admin/_base.scss +29 -29
  7. data/app/assets/stylesheets/active_admin/_header.scss +3 -5
  8. data/app/assets/stylesheets/active_admin/_mixins.scss +1 -1
  9. data/{vendor → app}/assets/stylesheets/active_admin/_normalize.scss +0 -0
  10. data/app/assets/stylesheets/active_admin/components/_tables.scss +1 -2
  11. data/app/assets/stylesheets/active_admin/mixins/_all.scss +8 -8
  12. data/app/assets/stylesheets/active_admin/mixins/_variables.scss +5 -0
  13. data/app/assets/stylesheets/active_admin/print.scss +2 -2
  14. data/app/assets/stylesheets/active_admin/structure/_main_structure.scss +1 -1
  15. data/app/javascript/active_admin/base.js +28 -0
  16. data/app/{assets/javascripts/active_admin/ext/jquery-ui.es6 → javascript/active_admin/ext/jquery-ui.js} +0 -0
  17. data/app/{assets/javascripts/active_admin/ext/jquery.es6 → javascript/active_admin/ext/jquery.js} +0 -0
  18. data/app/{assets/javascripts/active_admin/lib/batch_actions.es6 → javascript/active_admin/initializers/batch-actions.js} +9 -3
  19. data/app/javascript/active_admin/initializers/checkbox-toggler.js +3 -0
  20. data/app/{assets/javascripts/active_admin/initializers/datepicker.es6 → javascript/active_admin/initializers/datepicker.js} +0 -0
  21. data/app/javascript/active_admin/initializers/dropdown-menu.js +9 -0
  22. data/app/javascript/active_admin/initializers/filters.js +10 -0
  23. data/app/{assets/javascripts/active_admin/lib/has_many.es6 → javascript/active_admin/initializers/has-many.js} +0 -0
  24. data/app/javascript/active_admin/initializers/per-page.js +13 -0
  25. data/app/javascript/active_admin/initializers/table-checkbox-toggler.js +3 -0
  26. data/app/{assets/javascripts/active_admin/initializers/tabs.es6 → javascript/active_admin/initializers/tabs.js} +0 -0
  27. data/app/{assets/javascripts/active_admin/lib/checkbox-toggler.es6 → javascript/active_admin/lib/checkbox-toggler.js} +2 -2
  28. data/app/{assets/javascripts/active_admin/lib/dropdown-menu.es6 → javascript/active_admin/lib/dropdown-menu.js} +2 -9
  29. data/app/javascript/active_admin/lib/filters.js +39 -0
  30. data/app/{assets/javascripts/active_admin/lib/modal_dialog.es6 → javascript/active_admin/lib/modal-dialog.js} +3 -1
  31. data/app/javascript/active_admin/lib/per-page.js +38 -0
  32. data/app/{assets/javascripts/active_admin/lib/table-checkbox-toggler.es6 → javascript/active_admin/lib/table-checkbox-toggler.js} +4 -2
  33. data/app/javascript/active_admin/lib/utils.js +40 -0
  34. data/app/views/kaminari/active_admin_countless/_first_page.html.erb +11 -0
  35. data/app/views/kaminari/active_admin_countless/_gap.html.erb +8 -0
  36. data/app/views/kaminari/active_admin_countless/_next_page.html.erb +11 -0
  37. data/app/views/kaminari/active_admin_countless/_page.html.erb +12 -0
  38. data/app/views/kaminari/active_admin_countless/_paginator.html.erb +24 -0
  39. data/app/views/kaminari/active_admin_countless/_prev_page.html.erb +11 -0
  40. data/config/locales/az.yml +138 -0
  41. data/config/locales/ca.yml +0 -1
  42. data/config/locales/de.yml +18 -0
  43. data/config/locales/es.yml +3 -3
  44. data/config/locales/fr.yml +4 -4
  45. data/config/locales/sk.yml +59 -0
  46. data/docs/1-general-configuration.md +20 -0
  47. data/docs/2-resource-customization.md +1 -1
  48. data/docs/3-index-pages.md +1 -1
  49. data/docs/3-index-pages/index-as-table.md +7 -0
  50. data/docs/9-batch-actions.md +2 -2
  51. data/docs/Gemfile +0 -1
  52. data/docs/Gemfile.lock +103 -105
  53. data/docs/_config.yml +2 -0
  54. data/docs/_includes/top-menu.html +2 -2
  55. data/docs/index.html +108 -7
  56. data/docs/stylesheets/main.css +29 -0
  57. data/lib/active_admin.rb +0 -1
  58. data/lib/active_admin/application.rb +1 -1
  59. data/lib/active_admin/csv_builder.rb +1 -2
  60. data/lib/active_admin/filters/active_filter.rb +1 -1
  61. data/lib/active_admin/filters/resource_extension.rb +24 -0
  62. data/lib/active_admin/generators/boilerplate.rb +12 -4
  63. data/lib/active_admin/inputs/filters/date_range_input.rb +15 -12
  64. data/lib/active_admin/namespace_settings.rb +13 -0
  65. data/lib/active_admin/order_clause.rb +1 -1
  66. data/lib/active_admin/resource.rb +14 -1
  67. data/lib/active_admin/resource/belongs_to.rb +3 -0
  68. data/lib/active_admin/resource/model.rb +15 -0
  69. data/lib/active_admin/resource/routes.rb +11 -3
  70. data/lib/active_admin/resource_controller.rb +2 -0
  71. data/lib/active_admin/resource_controller/decorators.rb +2 -2
  72. data/lib/active_admin/resource_controller/polymorphic_routes.rb +37 -0
  73. data/lib/active_admin/version.rb +1 -1
  74. data/lib/active_admin/views/components/paginated_collection.rb +3 -2
  75. data/lib/active_admin/views/components/table_for.rb +1 -0
  76. data/lib/active_admin/views/index_as_table.rb +7 -0
  77. data/lib/active_admin/views/pages/base.rb +5 -3
  78. data/lib/active_admin/views/pages/index.rb +1 -0
  79. data/lib/generators/active_admin/install/templates/active_admin.rb.erb +14 -1
  80. data/lib/generators/active_admin/resource/templates/admin.rb.erb +4 -2
  81. metadata +35 -43
  82. data/app/assets/images/active_admin/nested_menu_arrow.gif +0 -0
  83. data/app/assets/images/active_admin/nested_menu_arrow_dark.gif +0 -0
  84. data/app/assets/images/active_admin/orderable.png +0 -0
  85. data/app/assets/javascripts/active_admin/base.es6 +0 -23
  86. data/app/assets/javascripts/active_admin/initializers/filters.es6 +0 -45
  87. data/app/assets/javascripts/active_admin/lib/active_admin.es6 +0 -41
  88. data/app/assets/javascripts/active_admin/lib/per_page.es6 +0 -47
@@ -615,6 +615,35 @@ body #tidelift a .cta {
615
615
  padding-left: 30px;
616
616
  }
617
617
 
618
+ body .tidelift-buttons a {
619
+ display: table;
620
+ width: 200px;
621
+ border: 2px solid #407985;
622
+ border-radius: 4px;
623
+ text-decoration: none;
624
+ font-family: 'Yanone Kaffeesatz', 'Helvetica Neue', Arial, Helvetica, sans-serif;
625
+ font-size: 18px;
626
+ letter-spacing: 1px;
627
+ margin: 0 10px;
628
+ }
629
+
630
+ body .tidelift-buttons a:first-child {
631
+ float: left;
632
+ color: #407985;
633
+ background: #FFF;
634
+ }
635
+
636
+ body .tidelift-buttons a:last-child {
637
+ color: #FFF;
638
+ background: #407985;
639
+ }
640
+
641
+ body .tidelift-buttons a span {
642
+ display: table-cell;
643
+ vertical-align: middle;
644
+ text-align: center;
645
+ }
646
+
618
647
  body .clear {
619
648
  clear: both;
620
649
  }
@@ -10,7 +10,6 @@ require 'inherited_resources'
10
10
  require 'jquery-rails'
11
11
  require 'sassc-rails'
12
12
  require 'arbre'
13
- require 'sprockets/es6'
14
13
 
15
14
  require 'active_admin/helpers/i18n'
16
15
 
@@ -73,7 +73,7 @@ module ActiveAdmin
73
73
  def namespace(name)
74
74
  name ||= :root
75
75
 
76
- namespace = namespaces[name] ||= begin
76
+ namespace = namespaces[name.to_sym] ||= begin
77
77
  namespace = Namespace.new(self, name)
78
78
  ActiveSupport::Notifications.publish ActiveAdmin::Namespace::RegisterEvent, namespace
79
79
  namespace
@@ -33,7 +33,7 @@ module ActiveAdmin
33
33
  def initialize(options = {}, &block)
34
34
  @resource = options.delete(:resource)
35
35
  @columns = []
36
- @options = options
36
+ @options = ActiveAdmin.application.csv_options.merge options
37
37
  @block = block
38
38
  end
39
39
 
@@ -44,7 +44,6 @@ module ActiveAdmin
44
44
  def build(controller, csv)
45
45
  @collection = controller.send :find_collection, except: :pagination
46
46
  columns = exec_columns controller.view_context
47
- options = ActiveAdmin.application.csv_options.merge self.options
48
47
  bom = options.delete :byte_order_mark
49
48
  column_names = options.delete(:column_names) { true }
50
49
  csv_options = options.except :encoding_options, :humanize_name
@@ -96,7 +96,7 @@ module ActiveAdmin
96
96
  end
97
97
 
98
98
  def filter
99
- resource.filters[name.to_sym]
99
+ resource.filters[name.to_sym] || resource.filters[condition.key.to_sym]
100
100
  end
101
101
 
102
102
  def related_primary_key
@@ -129,12 +129,36 @@ module ActiveAdmin
129
129
  not_poly.reject! { |r| r.chain.length > 2 }
130
130
 
131
131
  filters = poly.map(&:foreign_type) + not_poly.map(&:name)
132
+
133
+ # Check high-arity associations for filterable columns
134
+ max = namespace.maximum_association_filter_arity
135
+ if max != :unlimited
136
+ high_arity, low_arity = not_poly.partition do |r|
137
+ r.klass.reorder(nil).limit(max + 1).count > max
138
+ end
139
+
140
+ # Remove high-arity associations with no searchable column
141
+ high_arity = high_arity.select(&method(:searchable_column_for))
142
+
143
+ high_arity = high_arity.map { |r| r.name.to_s + "_" + searchable_column_for(r) + namespace.filter_method_for_large_association }
144
+
145
+ filters = poly.map(&:foreign_type) + low_arity.map(&:name) + high_arity
146
+ end
147
+
132
148
  filters.map &:to_sym
133
149
  else
134
150
  []
135
151
  end
136
152
  end
137
153
 
154
+ def search_columns
155
+ @search_columns ||= namespace.filter_columns_for_large_association.map(&:to_s)
156
+ end
157
+
158
+ def searchable_column_for(relation)
159
+ relation.klass.column_names.find { |name| search_columns.include?(name) }
160
+ end
161
+
138
162
  def add_filters_sidebar_section
139
163
  self.sidebar_sections << filters_sidebar_section
140
164
  end
@@ -9,8 +9,16 @@ module ActiveAdmin
9
9
  @class_name.constantize.new.attributes.keys
10
10
  end
11
11
 
12
+ def assignable_attributes
13
+ attributes - %w(id created_at updated_at)
14
+ end
15
+
16
+ def permit_params
17
+ assignable_attributes.map { |a| a.to_sym.inspect }.join(', ')
18
+ end
19
+
12
20
  def rows
13
- attributes.map { |a| row(a) }.join("\n")
21
+ attributes.map { |a| row(a) }.join("\n ")
14
22
  end
15
23
 
16
24
  def row(name)
@@ -18,7 +26,7 @@ module ActiveAdmin
18
26
  end
19
27
 
20
28
  def columns
21
- attributes.map { |a| column(a) }.join("\n")
29
+ attributes.map { |a| column(a) }.join("\n ")
22
30
  end
23
31
 
24
32
  def column(name)
@@ -26,7 +34,7 @@ module ActiveAdmin
26
34
  end
27
35
 
28
36
  def filters
29
- attributes.map { |a| filter(a) }.join("\n")
37
+ attributes.map { |a| filter(a) }.join("\n ")
30
38
  end
31
39
 
32
40
  def filter(name)
@@ -34,7 +42,7 @@ module ActiveAdmin
34
42
  end
35
43
 
36
44
  def form_inputs
37
- attributes.reject { |a| %w(id created_at updated_at).include? a }.map { |a| form_input(a) }.join("\n")
45
+ assignable_attributes.map { |a| form_input(a) }.join("\n ")
38
46
  end
39
47
 
40
48
  def form_input(name)
@@ -7,8 +7,8 @@ module ActiveAdmin
7
7
  def to_html
8
8
  input_wrapping do
9
9
  [ label_html,
10
- builder.text_field(gt_input_name, input_html_options(gt_input_name, gt_input_placeholder)),
11
- builder.text_field(lt_input_name, input_html_options(lt_input_name, lt_input_placeholder)),
10
+ builder.text_field(gt_input_name, input_html_options_for(gt_input_name, gt_input_placeholder)),
11
+ builder.text_field(lt_input_name, input_html_options_for(lt_input_name, lt_input_placeholder)),
12
12
  ].join("\n").html_safe
13
13
  end
14
14
  end
@@ -22,18 +22,21 @@ module ActiveAdmin
22
22
  column && column.type == :date ? "#{method}_lteq" : "#{method}_lteq_datetime"
23
23
  end
24
24
 
25
- def input_html_options(input_name = gt_input_name, placeholder = gt_input_placeholder)
26
- current_value = begin
27
- #cast value to date object before rendering input
28
- @object.public_send(input_name).to_s.to_date
29
- rescue
30
- nil
31
- end
25
+ def input_html_options
32
26
  { size: 12,
33
27
  class: "datepicker",
34
- maxlength: 10,
35
- placeholder: placeholder,
36
- value: current_value ? current_value.strftime("%Y-%m-%d") : "" }
28
+ maxlength: 10 }.merge(options[:input_html] || {})
29
+ end
30
+
31
+ def input_html_options_for(input_name, placeholder)
32
+ current_value = begin
33
+ #cast value to date object before rendering input
34
+ @object.public_send(input_name).to_s.to_date
35
+ rescue
36
+ nil
37
+ end
38
+ { placeholder: placeholder,
39
+ value: current_value ? current_value.strftime("%Y-%m-%d") : "" }.merge(input_html_options)
37
40
  end
38
41
 
39
42
  def gt_input_placeholder
@@ -106,5 +106,18 @@ module ActiveAdmin
106
106
 
107
107
  # Include association filters by default
108
108
  register :include_default_association_filters, true
109
+
110
+ register :maximum_association_filter_arity, :unlimited
111
+
112
+ register :filter_columns_for_large_association, [
113
+ :display_name,
114
+ :full_name,
115
+ :name,
116
+ :username,
117
+ :login,
118
+ :title,
119
+ :email,
120
+ ]
121
+ register :filter_method_for_large_association, '_starts_with'
109
122
  end
110
123
  end
@@ -3,7 +3,7 @@ module ActiveAdmin
3
3
  attr_reader :field, :order, :active_admin_config
4
4
 
5
5
  def initialize(active_admin_config, clause)
6
- clause =~ /^([\w\_\.]+)(->'\w+')?_(desc|asc)$/
6
+ clause =~ /^([\w\.]+)(->'\w+')?_(desc|asc)$/
7
7
  @column = $1
8
8
  @op = $2
9
9
  @order = $3
@@ -12,6 +12,7 @@ require 'active_admin/resource/scope_to'
12
12
  require 'active_admin/resource/sidebars'
13
13
  require 'active_admin/resource/belongs_to'
14
14
  require 'active_admin/resource/ordering'
15
+ require 'active_admin/resource/model'
15
16
 
16
17
  module ActiveAdmin
17
18
 
@@ -69,7 +70,7 @@ module ActiveAdmin
69
70
  def initialize(namespace, resource_class, options = {})
70
71
  @namespace = namespace
71
72
  @resource_class_name = "::#{resource_class.name}"
72
- @options = options
73
+ @options = options
73
74
  @sort_order = options[:sort_order]
74
75
  @member_actions = []
75
76
  @collection_actions = []
@@ -104,6 +105,10 @@ module ActiveAdmin
104
105
  ActiveSupport::Dependencies.constantize(decorator_class_name) if decorator_class_name
105
106
  end
106
107
 
108
+ def resource_name_extension
109
+ @resource_name_extension ||= define_resource_name_extension(self)
110
+ end
111
+
107
112
  def resource_table_name
108
113
  resource_class.quoted_table_name
109
114
  end
@@ -133,6 +138,7 @@ module ActiveAdmin
133
138
  def belongs_to(target, options = {})
134
139
  @belongs_to = Resource::BelongsTo.new(self, target, options)
135
140
  self.menu_item_options = false if @belongs_to.required?
141
+ options[:class_name] ||= @belongs_to.resource.resource_class_name if @belongs_to.resource
136
142
  controller.send :belongs_to, target, options.dup
137
143
  end
138
144
 
@@ -203,5 +209,12 @@ module ActiveAdmin
203
209
  @default_csv_builder ||= CSVBuilder.default_for_resource(self)
204
210
  end
205
211
 
212
+ def define_resource_name_extension(resource)
213
+ Module.new do
214
+ define_method :model_name do
215
+ resource.resource_name
216
+ end
217
+ end
218
+ end
206
219
  end # class Resource
207
220
  end # module ActiveAdmin
@@ -14,6 +14,9 @@ module ActiveAdmin
14
14
  # The resource which initiated this relationship
15
15
  attr_reader :owner
16
16
 
17
+ # The name of the relation
18
+ attr_reader :target_name
19
+
17
20
  def initialize(owner, target_name, options = {})
18
21
  @owner = owner
19
22
  @target_name = target_name
@@ -0,0 +1,15 @@
1
+ module ActiveAdmin
2
+ class Model
3
+ def initialize(resource, record)
4
+ @record = record
5
+
6
+ if resource
7
+ @record.extend(resource.resource_name_extension)
8
+ end
9
+ end
10
+
11
+ def to_model
12
+ @record
13
+ end
14
+ end
15
+ end
@@ -110,7 +110,7 @@ module ActiveAdmin
110
110
  # @return params to pass to instance path
111
111
  def route_instance_params(instance)
112
112
  if nested?
113
- [instance.public_send(belongs_to_name).to_param, instance.to_param]
113
+ [instance.public_send(belongs_to_target_name).to_param, instance.to_param]
114
114
  else
115
115
  instance.to_param
116
116
  end
@@ -123,11 +123,19 @@ module ActiveAdmin
123
123
  end
124
124
 
125
125
  def nested?
126
- resource.belongs_to? && resource.belongs_to_config.required?
126
+ resource.belongs_to? && belongs_to_config.required?
127
+ end
128
+
129
+ def belongs_to_target_name
130
+ belongs_to_config.target_name
127
131
  end
128
132
 
129
133
  def belongs_to_name
130
- resource.belongs_to_config.target.resource_name.singular if nested?
134
+ belongs_to_config.target.resource_name.singular
135
+ end
136
+
137
+ def belongs_to_config
138
+ resource.belongs_to_config
131
139
  end
132
140
 
133
141
  def routes
@@ -1,6 +1,7 @@
1
1
  require 'active_admin/resource_controller/action_builder'
2
2
  require 'active_admin/resource_controller/data_access'
3
3
  require 'active_admin/resource_controller/decorators'
4
+ require 'active_admin/resource_controller/polymorphic_routes'
4
5
  require 'active_admin/resource_controller/scoping'
5
6
  require 'active_admin/resource_controller/streaming'
6
7
  require 'active_admin/resource_controller/sidebars'
@@ -18,6 +19,7 @@ module ActiveAdmin
18
19
  include ActionBuilder
19
20
  include Decorators
20
21
  include DataAccess
22
+ include PolymorphicRoutes
21
23
  include Scoping
22
24
  include Streaming
23
25
  include Sidebars
@@ -67,8 +67,8 @@ module ActiveAdmin
67
67
  def self.wrap!(parent, name)
68
68
  ::Class.new parent do
69
69
  delegate :reorder, :page, :current_page, :total_pages, :limit_value,
70
- :total_count, :total_pages, :to_key, :group_values, :except,
71
- :find_each, :ransack
70
+ :total_count, :total_pages, :offset, :to_key, :group_values,
71
+ :except, :find_each, :ransack
72
72
 
73
73
  define_singleton_method(:name) { name }
74
74
  end
@@ -0,0 +1,37 @@
1
+ require "active_admin/resource"
2
+ require "active_admin/resource/model"
3
+
4
+ module ActiveAdmin
5
+ class ResourceController < BaseController
6
+ module PolymorphicRoutes
7
+ def polymorphic_url(record_or_hash_or_array, options = {})
8
+ super(map_named_resources_for(record_or_hash_or_array), options)
9
+ end
10
+
11
+ def polymorphic_path(record_or_hash_or_array, options = {})
12
+ super(map_named_resources_for(record_or_hash_or_array), options)
13
+ end
14
+
15
+ private
16
+
17
+ def map_named_resources_for(record_or_hash_or_array)
18
+ return record_or_hash_or_array unless record_or_hash_or_array.is_a?(Array)
19
+
20
+ record_or_hash_or_array.map { |record| to_named_resource(record) }
21
+ end
22
+
23
+ def to_named_resource(record)
24
+ if record.is_a?(resource_class)
25
+ return ActiveAdmin::Model.new(active_admin_config, record)
26
+ end
27
+
28
+ belongs_to_resource = active_admin_config.belongs_to_config.try(:resource)
29
+ if belongs_to_resource && record.is_a?(belongs_to_resource.resource_class)
30
+ return ActiveAdmin::Model.new(belongs_to_resource, record)
31
+ end
32
+
33
+ record
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveAdmin
2
- VERSION = '2.2.0'
2
+ VERSION = '2.6.0'
3
3
  end
@@ -92,11 +92,11 @@ module ActiveAdmin
92
92
  end
93
93
 
94
94
  def build_pagination
95
- options = { theme: 'active_admin' }
95
+ options = { theme: @display_total ? 'active_admin' : 'active_admin_countless' }
96
96
  options[:params] = @params if @params
97
97
  options[:param_name] = @param_name if @param_name
98
98
 
99
- if !@display_total && collection.respond_to?(:offset)
99
+ if !@display_total
100
100
  # The #paginate method in kaminari will query the resource with a
101
101
  # count(*) to determine how many pages there should be unless
102
102
  # you pass in the :total_pages option. We issue a query to determine
@@ -123,6 +123,7 @@ module ActiveAdmin
123
123
  entries_name = I18n.t "active_admin.pagination.entry", count: 2, default: 'entries'
124
124
  else
125
125
  key = "activerecord.models." + collection.first.class.model_name.i18n_key.to_s
126
+
126
127
  entry_name = I18n.translate key, count: 1, default: collection.first.class.name.underscore.sub('_', ' ')
127
128
  entries_name = I18n.translate key, count: collection.size, default: entry_name.pluralize
128
129
  end
@@ -13,6 +13,7 @@ module ActiveAdmin
13
13
  @collection = obj.respond_to?(:each) && !obj.is_a?(Hash) ? obj : [obj]
14
14
  @resource_class = options.delete(:i18n)
15
15
  @resource_class ||= @collection.klass if @collection.respond_to? :klass
16
+
16
17
  @columns = []
17
18
  @row_class = options.delete(:row_class)
18
19
 
@@ -192,6 +192,13 @@ module ActiveAdmin
192
192
  # end
193
193
  # ```
194
194
  #
195
+ # You can also define associated objects to include outside of the
196
+ # `scoped_collection` method:
197
+ #
198
+ # ```ruby
199
+ # includes :publisher
200
+ # ```
201
+ #
195
202
  # Then it's simple to sort by any Publisher attribute from within the index table:
196
203
  #
197
204
  # ```ruby