active_scaffold 3.6.20 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +47 -0
  3. data/README.md +29 -16
  4. data/app/assets/javascripts/jquery/active_scaffold.js +106 -68
  5. data/app/assets/javascripts/jquery/active_scaffold_chosen.js +6 -5
  6. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +18 -4
  7. data/app/assets/stylesheets/active_scaffold_layout.css +13 -2
  8. data/app/views/active_scaffold_overrides/_base_form.html.erb +5 -1
  9. data/app/views/active_scaffold_overrides/_field_search.html.erb +1 -0
  10. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +2 -1
  11. data/app/views/active_scaffold_overrides/_render_field.js.erb +24 -13
  12. data/config/locales/de.yml +6 -3
  13. data/config/locales/en.yml +3 -0
  14. data/config/locales/es.yml +3 -0
  15. data/config/locales/fr.yml +9 -6
  16. data/config/locales/hu.yml +20 -17
  17. data/config/locales/ja.yml +83 -80
  18. data/config/locales/ru.yml +17 -14
  19. data/lib/active_scaffold/actions/common_search.rb +2 -2
  20. data/lib/active_scaffold/actions/core.rb +30 -10
  21. data/lib/active_scaffold/actions/field_search.rb +9 -6
  22. data/lib/active_scaffold/actions/nested.rb +7 -7
  23. data/lib/active_scaffold/actions/update.rb +3 -3
  24. data/lib/active_scaffold/attribute_params.rb +22 -70
  25. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +0 -3
  26. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +6 -6
  27. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +7 -7
  28. data/lib/active_scaffold/bridges/active_storage.rb +3 -0
  29. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +2 -2
  30. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +2 -2
  31. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +12 -14
  32. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +2 -2
  33. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +1 -1
  34. data/lib/active_scaffold/bridges/chosen/helpers.rb +10 -10
  35. data/lib/active_scaffold/bridges/country_select/country_select_bridge_helper.rb +7 -7
  36. data/lib/active_scaffold/bridges/date_picker/ext.rb +20 -9
  37. data/lib/active_scaffold/bridges/date_picker/helper.rb +9 -9
  38. data/lib/active_scaffold/bridges/date_picker.rb +2 -0
  39. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +3 -3
  40. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +5 -5
  41. data/lib/active_scaffold/bridges/file_column/form_ui.rb +1 -1
  42. data/lib/active_scaffold/bridges/file_column/list_ui.rb +3 -3
  43. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +1 -1
  44. data/lib/active_scaffold/bridges/paper_trail/actions.rb +4 -1
  45. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +3 -3
  46. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  47. data/lib/active_scaffold/bridges/record_select/helpers.rb +17 -17
  48. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +6 -6
  49. data/lib/active_scaffold/bridges/tiny_mce.rb +1 -1
  50. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +6 -11
  51. data/lib/active_scaffold/bridges.rb +0 -3
  52. data/lib/active_scaffold/config/core.rb +1 -1
  53. data/lib/active_scaffold/config/field_search.rb +9 -1
  54. data/lib/active_scaffold/config/form.rb +9 -1
  55. data/lib/active_scaffold/constraints.rb +22 -7
  56. data/lib/active_scaffold/core.rb +6 -10
  57. data/lib/active_scaffold/data_structures/action_columns.rb +0 -25
  58. data/lib/active_scaffold/data_structures/action_links.rb +1 -1
  59. data/lib/active_scaffold/data_structures/association/abstract.rb +8 -0
  60. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +8 -0
  61. data/lib/active_scaffold/data_structures/association/active_record.rb +1 -13
  62. data/lib/active_scaffold/data_structures/association/mongoid.rb +21 -8
  63. data/lib/active_scaffold/data_structures/column.rb +139 -28
  64. data/lib/active_scaffold/data_structures/columns.rb +12 -12
  65. data/lib/active_scaffold/data_structures/nested_info.rb +12 -0
  66. data/lib/active_scaffold/data_structures/sorting.rb +1 -1
  67. data/lib/active_scaffold/engine.rb +15 -1
  68. data/lib/active_scaffold/extensions/action_view_rendering.rb +13 -5
  69. data/lib/active_scaffold/extensions/cow_proxy.rb +1 -1
  70. data/lib/active_scaffold/extensions/routing_mapper.rb +1 -0
  71. data/lib/active_scaffold/extensions/unsaved_record.rb +9 -3
  72. data/lib/active_scaffold/finder.rb +147 -28
  73. data/lib/active_scaffold/helpers/action_link_helpers.rb +1 -1
  74. data/lib/active_scaffold/helpers/controller_helpers.rb +9 -4
  75. data/lib/active_scaffold/helpers/form_column_helpers.rb +153 -107
  76. data/lib/active_scaffold/helpers/human_condition_helpers.rb +48 -14
  77. data/lib/active_scaffold/helpers/list_column_helpers.rb +37 -20
  78. data/lib/active_scaffold/helpers/search_column_helpers.rb +137 -55
  79. data/lib/active_scaffold/helpers/show_column_helpers.rb +6 -6
  80. data/lib/active_scaffold/helpers/view_helpers.rb +1 -1
  81. data/lib/active_scaffold/orm_checks.rb +21 -1
  82. data/lib/active_scaffold/registry.rb +10 -15
  83. data/lib/active_scaffold/tableless.rb +10 -79
  84. data/lib/active_scaffold/version.rb +2 -2
  85. data/lib/active_scaffold.rb +3 -9
  86. data/lib/generators/active_scaffold/install_generator.rb +2 -2
  87. data/test/bridges/bridge_test.rb +1 -1
  88. data/test/bridges/date_picker_test.rb +3 -2
  89. data/test/bridges/paperclip_test.rb +18 -14
  90. data/test/bridges/tiny_mce_test.rb +5 -3
  91. data/test/config/base_test.rb +1 -1
  92. data/test/config/core_test.rb +1 -1
  93. data/test/config/create_test.rb +1 -1
  94. data/test/config/delete_test.rb +1 -1
  95. data/test/config/field_search_test.rb +1 -1
  96. data/test/config/list_test.rb +1 -1
  97. data/test/config/nested_test.rb +1 -1
  98. data/test/config/search_test.rb +1 -1
  99. data/test/config/show_test.rb +1 -1
  100. data/test/config/subform_test.rb +1 -1
  101. data/test/config/update_test.rb +1 -1
  102. data/test/data_structures/action_columns_test.rb +1 -1
  103. data/test/data_structures/action_link_test.rb +1 -1
  104. data/test/data_structures/action_links_test.rb +1 -1
  105. data/test/data_structures/actions_test.rb +1 -1
  106. data/test/data_structures/association_column_test.rb +1 -1
  107. data/test/data_structures/column_test.rb +1 -1
  108. data/test/data_structures/columns_test.rb +1 -1
  109. data/test/data_structures/set_test.rb +1 -1
  110. data/test/data_structures/sorting_test.rb +1 -1
  111. data/test/data_structures/standard_column_test.rb +1 -1
  112. data/test/data_structures/validation_reflection_test.rb +1 -1
  113. data/test/data_structures/virtual_column_test.rb +1 -1
  114. data/test/extensions/active_record_test.rb +1 -1
  115. data/test/helpers/form_column_helpers_test.rb +7 -5
  116. data/test/helpers/pagination_helpers_test.rb +1 -1
  117. data/test/helpers/search_column_helpers_test.rb +2 -1
  118. data/test/misc/active_record_permissions_test.rb +1 -1
  119. data/test/misc/attribute_params_test.rb +1 -1
  120. data/test/misc/calculation_test.rb +1 -1
  121. data/test/misc/configurable_test.rb +1 -1
  122. data/test/misc/constraints_test.rb +2 -1
  123. data/test/misc/convert_numbers_format_test.rb +1 -1
  124. data/test/misc/finder_test.rb +39 -1
  125. data/test/misc/lang_test.rb +1 -1
  126. data/test/misc/parse_datetime_test.rb +1 -1
  127. data/test/misc/tableless_test.rb +1 -1
  128. data/test/test_helper.rb +4 -4
  129. metadata +5 -17
  130. data/config/brakeman.ignore +0 -26
  131. data/config/brakeman.yml +0 -3
  132. data/config/i18n-tasks.yml +0 -121
  133. data/lib/active_scaffold/bridges/shared/date_bridge.rb +0 -221
  134. data/lib/active_scaffold/delayed_setup.rb +0 -41
  135. data/lib/active_scaffold/extensions/left_outer_joins.rb +0 -43
@@ -19,7 +19,7 @@ module ActiveScaffold
19
19
 
20
20
  # second, check if the dev has specified a valid form_ui for this column
21
21
  elsif column.form_ui && (method = override_input(column.form_ui))
22
- send(method, column, options)
22
+ send(method, column, options, ui_options: (column.form_ui_options || column.options).except(:collapsible))
23
23
 
24
24
  elsif column.association
25
25
  # if we get here, it's because the column has a form_ui but not one ActiveScaffold knows about.
@@ -32,16 +32,15 @@ module ActiveScaffold
32
32
  options[:value] = format_number_value(record.send(column.name), column.options) if column.number?
33
33
  active_scaffold_input_virtual(column, options)
34
34
 
35
- elsif (method = override_input(column.column.type)) # regular model attribute column
35
+ elsif (method = override_input(column.column_type)) # regular model attribute column
36
36
  # if we (or someone else) have created a custom render option for the column type, use that
37
37
  send(method, column, options)
38
38
 
39
39
  else # final ultimate fallback: use rails' generic input method
40
40
  # for textual fields we pass different options
41
- text_types = %i[text string integer float decimal]
42
- options = active_scaffold_input_text_options(options) if text_types.include?(column.column.type)
43
- if column.column.type == :string && options[:maxlength].blank?
44
- options[:maxlength] = column.column.limit
41
+ options = active_scaffold_input_text_options(options) if column.text? || column.number?
42
+ if column.column_type == :string && options[:maxlength].blank?
43
+ options[:maxlength] = column.type_for_attribute.limit
45
44
  options[:size] ||= options[:maxlength].to_i > 30 ? 30 : options[:maxlength]
46
45
  end
47
46
  options[:value] = format_number_value(record.send(column.name), column.options) if column.number?
@@ -107,7 +106,11 @@ module ActiveScaffold
107
106
  classes = "#{column.name}-input"
108
107
  classes += ' numeric-input' if column.number?
109
108
 
110
- {:name => name, :class => classes, :id => id_control}.merge(options)
109
+ if (column.form_ui_options || column.options)[:collapsible]
110
+ collapsible_id = "container_#{id_control}"
111
+ end
112
+
113
+ {name: name, class: classes, id: id_control, collapsible_id: collapsible_id}.merge(options)
111
114
  end
112
115
 
113
116
  def current_form_columns(record, scope, subform_controller = nil)
@@ -120,15 +123,15 @@ module ActiveScaffold
120
123
  end
121
124
  end
122
125
 
123
- def update_columns_options(column, scope, options, force = false)
126
+ def update_columns_options(column, scope, options, force = false, form_columns: nil, url_params: {})
124
127
  record = options[:object]
125
128
  subform_controller = controller.class.active_scaffold_controller_for(record.class) if scope
126
129
  if @main_columns && (scope.nil? || subform_controller == controller.class)
127
- form_columns = @main_columns.visible_columns_names
130
+ form_columns ||= @main_columns.visible_columns_names
128
131
  end
129
132
  form_columns ||= current_form_columns(record, scope, subform_controller)
130
133
  if force || (form_columns && column.update_columns && (column.update_columns & form_columns).present?)
131
- url_params = params_for(:action => 'render_field', :column => column.name, :id => record.to_param)
134
+ url_params.reverse_merge! params_for(action: 'render_field', column: column.name, id: record.to_param)
132
135
  if nested? && scope
133
136
  url_params[:nested] = url_params.slice(:parent_scaffold, :association, nested.param_name)
134
137
  url_params = url_params.except(:parent_scaffold, :association, nested.param_name)
@@ -153,7 +156,14 @@ module ActiveScaffold
153
156
  end
154
157
 
155
158
  def render_column(column, record, renders_as, scope = nil, only_value = false, col_class = nil) # rubocop:disable Metrics/ParameterLists
156
- if (partial = override_form_field_partial(column))
159
+ if form_column_is_hidden?(column, record, scope)
160
+ # creates an element that can be replaced by the update_columns routine,
161
+ # but will not affect the value of the submitted form in this state:
162
+ # <dl><input type="hidden" class="<%= column.name %>-input"></dl>
163
+ content_tag :dl, style: 'display: none' do
164
+ hidden_field_tag(nil, nil, :class => "#{column.name}-input")
165
+ end
166
+ elsif (partial = override_form_field_partial(column))
157
167
  render :partial => partial, :locals => {:column => column, :only_value => only_value, :scope => scope, :col_class => col_class, :record => record}
158
168
  elsif renders_as == :field || override_form_field?(column)
159
169
  form_attribute(column, record, scope, only_value, col_class)
@@ -164,8 +174,19 @@ module ActiveScaffold
164
174
  end
165
175
  end
166
176
 
177
+ def form_column_is_hidden?(column, record, scope = nil)
178
+ if column.hide_form_column_if&.respond_to?(:call)
179
+ column.hide_form_column_if.call(record, column, scope)
180
+ elsif column.hide_form_column_if&.is_a?(Symbol)
181
+ record.send(column.hide_form_column_if)
182
+ else
183
+ column.hide_form_column_if
184
+ end
185
+ end
186
+
167
187
  def form_attribute(column, record, scope = nil, only_value = false, col_class = nil)
168
188
  column_options = active_scaffold_input_options(column, scope, :object => record)
189
+ collapsible_id = column_options.delete :collapsible_id
169
190
  attributes = field_attributes(column, record)
170
191
  attributes[:class] = "#{attributes[:class]} #{col_class}" if col_class.present?
171
192
  if only_value
@@ -181,12 +202,14 @@ module ActiveScaffold
181
202
  end
182
203
  if field
183
204
  field << loading_indicator_tag(:action => :render_field, :id => params[:id]) if column.update_columns
184
- field << content_tag(:span, column.description, :class => 'description') if column.description.present?
205
+ desc = column.description(record, scope)
206
+ field << content_tag(:span, desc, :class => 'description') if desc.present?
185
207
  end
186
208
 
209
+ label = label_tag(label_for(column, column_options), form_column_label(column, record, scope))
210
+ label << h(' ') << link_to_visibility_toggle(collapsible_id) if collapsible_id
187
211
  content_tag :dl, attributes do
188
- content_tag(:dt, label_tag(label_for(column, column_options), form_column_label(column))) <<
189
- content_tag(:dd, field)
212
+ content_tag(:dt, label) << content_tag(:dd, field, id: collapsible_id)
190
213
  end
191
214
  end
192
215
 
@@ -194,8 +217,8 @@ module ActiveScaffold
194
217
  options[:id] unless column.form_ui == :select && column.association&.collection?
195
218
  end
196
219
 
197
- def form_column_label(column)
198
- column.label
220
+ def form_column_label(column, record = nil, scope = nil)
221
+ column.label(record, scope)
199
222
  end
200
223
 
201
224
  def subform_label(column, hidden)
@@ -286,7 +309,7 @@ module ActiveScaffold
286
309
  options[:name] = "#{options[:name]}[]"
287
310
  end
288
311
 
289
- def active_scaffold_input_singular_association(column, html_options, options = {})
312
+ def active_scaffold_input_singular_association(column, html_options, options = {}, ui_options: column.options)
290
313
  record = html_options.delete(:object)
291
314
  associated = record.send(column.association.name)
292
315
 
@@ -296,27 +319,27 @@ module ActiveScaffold
296
319
  method = column.name
297
320
  options.merge! :selected => associated&.id, :include_blank => as_(:_select_), :object => record
298
321
 
299
- html_options.merge!(column.options[:html_options] || {})
300
- options.merge!(column.options)
301
- active_scaffold_select_name_with_multiple html_options
322
+ html_options.merge!(ui_options[:html_options] || {})
323
+ options.merge!(ui_options)
324
+ html_options.delete(:multiple) # no point using multiple in a form for singular assoc, but may be set for field search
302
325
  active_scaffold_translate_select_options(options)
303
326
 
304
327
  html =
305
328
  if (optgroup = options.delete(:optgroup))
306
329
  select(:record, method, active_scaffold_grouped_options(column, select_options, optgroup), options, html_options)
307
330
  else
308
- collection_select(:record, method, select_options, :id, column.options[:label_method] || :to_label, options, html_options)
331
+ collection_select(:record, method, select_options, :id, ui_options[:label_method] || :to_label, options, html_options)
309
332
  end
310
- html << active_scaffold_refresh_link(column, html_options, record) if column.options[:refresh_link]
311
- html << active_scaffold_new_record_subform(column, record, html_options) if column.options[:add_new]
333
+ html << active_scaffold_refresh_link(column, html_options, record, ui_options) if ui_options[:refresh_link]
334
+ html << active_scaffold_new_record_subform(column, record, html_options, ui_options: ui_options) if ui_options[:add_new]
312
335
  html
313
336
  end
314
337
 
315
- def active_scaffold_new_record_subform(column, record, html_options, new_record_attributes: nil, locals: {}, skip_link: false) # rubocop:disable Metrics/ParameterLists
338
+ def active_scaffold_new_record_subform(column, record, html_options, ui_options: column.options, new_record_attributes: nil, locals: {}, skip_link: false)
316
339
  klass =
317
340
  if column.association.polymorphic? && column.association.belongs_to?
318
341
  type = record.send(column.association.foreign_type)
319
- column.association.klass(record) if type.present? && (column.options[:add_new] == true || type.in?(column.options[:add_new]))
342
+ column.association.klass(record) if type.present? && (ui_options[:add_new] == true || type.in?(ui_options[:add_new]))
320
343
  else
321
344
  column.association.klass
322
345
  end
@@ -331,8 +354,8 @@ module ActiveScaffold
331
354
  scope = html_options[:name].scan(/record(.*)\[#{column.name}\]/).dig(0, 0)
332
355
  new_record ||= klass.new(new_record_attributes)
333
356
  locals = locals.reverse_merge(column: column, parent_record: record, associated: [], show_blank_record: new_record, scope: scope)
334
- subform = render(partial: subform_partial_for_column(column, klass), locals: locals)
335
- if column.options[:hide_subgroups]
357
+ subform = render(partial: subform_partial_for_column(column, klass, ui_options: ui_options), locals: locals)
358
+ if ui_options[:hide_subgroups]
336
359
  toggable_id = "#{sub_form_id(association: column.name, id: record.id || generated_id(record) || 99_999_999_999)}-div"
337
360
  subform << link_to_visibility_toggle(toggable_id, default_visible: false)
338
361
  end
@@ -347,8 +370,8 @@ module ActiveScaffold
347
370
  link_to(label, '#', data: data, class: 'show-new-subform')
348
371
  end
349
372
 
350
- def active_scaffold_file_with_remove_link(column, options, content, remove_file_prefix, controls_class, &block) # rubocop:disable Metrics/ParameterLists
351
- options = active_scaffold_input_text_options(options.merge(column.options))
373
+ def active_scaffold_file_with_remove_link(column, options, content, remove_file_prefix, controls_class, ui_options: column.options, &block) # rubocop:disable Metrics/ParameterLists
374
+ options = active_scaffold_input_text_options(options.merge(ui_options))
352
375
  if content
353
376
  active_scaffold_file_with_content(column, content, options, remove_file_prefix, controls_class, &block)
354
377
  else
@@ -381,7 +404,7 @@ module ActiveScaffold
381
404
  end
382
405
  end
383
406
 
384
- def active_scaffold_refresh_link(column, html_options, record)
407
+ def active_scaffold_refresh_link(column, html_options, record, ui_options = {})
385
408
  link_options = {:object => record}
386
409
  if html_options['data-update_url']
387
410
  link_options['data-update_send_form'] = html_options['data-update_send_form']
@@ -391,7 +414,12 @@ module ActiveScaffold
391
414
  link_options = update_columns_options(column, scope.presence, link_options, true)
392
415
  end
393
416
  link_options[:class] = 'refresh-link'
394
- link_to(as_(:refresh), link_options.delete('data-update_url') || html_options['data-update_url'], link_options)
417
+ if ui_options[:refresh_link].is_a?(Hash)
418
+ text = ui_options.dig(:refresh_link, :text)
419
+ text = as_(text) if text.is_a?(Symbol)
420
+ link_options.merge! ui_options[:refresh_link].except(:text)
421
+ end
422
+ link_to(text || as_(:refresh), link_options.delete('data-update_url') || html_options['data-update_url'], link_options.except(:object))
395
423
  end
396
424
 
397
425
  def active_scaffold_plural_association_options(column, record = nil)
@@ -399,21 +427,29 @@ module ActiveScaffold
399
427
  [associated_options, associated_options | sorted_association_options_find(column.association, nil, record)]
400
428
  end
401
429
 
402
- def active_scaffold_input_plural_association(column, options)
430
+ def active_scaffold_input_plural_association(column, options, ui_options: column.options)
403
431
  record = options.delete(:object)
404
432
  associated_options, select_options = active_scaffold_plural_association_options(column, record)
405
433
 
406
434
  html =
407
- if select_options.empty?
435
+ if options[:multiple] || ui_options.dig(:html_options, :multiple)
436
+ html_options = options.merge(ui_options[:html_options] || {})
437
+ active_scaffold_select_name_with_multiple html_options
438
+ collection_select(:record, column.name, select_options, :id, ui_options[:label_method] || :to_label, ui_options.merge(object: record), html_options)
439
+ elsif select_options.empty?
408
440
  content_tag(:span, as_(:no_options), :class => "#{options[:class]} no-options", :id => options[:id]) <<
409
441
  hidden_field_tag("#{options[:name]}[]", '', :id => nil)
410
442
  else
411
- active_scaffold_checkbox_list(column, select_options, associated_options.collect(&:id), options)
443
+ active_scaffold_checkbox_list(column, select_options, associated_options.collect(&:id), options, ui_options: ui_options)
412
444
  end
413
- html << active_scaffold_refresh_link(column, options, record) if column.options[:refresh_link]
445
+ html << active_scaffold_refresh_link(column, options, record, ui_options) if ui_options[:refresh_link]
414
446
  html
415
447
  end
416
448
 
449
+ def active_scaffold_input_draggable(column, options, ui_options: column.options)
450
+ active_scaffold_input_plural_association(column, options.merge(draggable_lists: true), ui_options: ui_options)
451
+ end
452
+
417
453
  def active_scaffold_checkbox_option(option, label_method, associated_ids, checkbox_options, li_options = {})
418
454
  content_tag(:li, li_options) do
419
455
  option_id = option.is_a?(Array) ? option[1] : option.id
@@ -423,10 +459,11 @@ module ActiveScaffold
423
459
  end
424
460
  end
425
461
 
426
- def active_scaffold_checkbox_list(column, select_options, associated_ids, options)
427
- label_method = column.options[:label_method] || :to_label
462
+ def active_scaffold_checkbox_list(column, select_options, associated_ids, options, ui_options: column.options)
463
+ label_method = ui_options[:label_method] || :to_label
428
464
  html = hidden_field_tag("#{options[:name]}[]", '', :id => nil)
429
- html << content_tag(:ul, options.merge(:class => "#{options[:class]} checkbox-list#{' draggable-lists' if column.options[:draggable_lists]}")) do
465
+ draggable = options.delete(:draggable_lists) || ui_options[:draggable_lists]
466
+ html << content_tag(:ul, options.merge(:class => "#{options[:class]} checkbox-list#{' draggable-lists' if draggable}")) do
430
467
  content = []
431
468
  select_options.each_with_index do |option, i|
432
469
  content << active_scaffold_checkbox_option(option, label_method, associated_ids, :name => "#{options[:name]}[]", :id => "#{options[:id]}_#{i}_id")
@@ -441,37 +478,43 @@ module ActiveScaffold
441
478
  [(text.is_a?(Symbol) ? column.active_record_class.human_attribute_name(text) : text), value]
442
479
  end
443
480
 
444
- def active_scaffold_enum_options(column, record = nil)
445
- column.options[:options]
481
+ def active_scaffold_enum_options(column, record = nil, ui_options: column.options)
482
+ ui_options[:options]
446
483
  end
447
484
 
448
- def active_scaffold_input_enum(column, html_options, options = {})
485
+ def active_scaffold_input_enum(column, html_options, options = {}, ui_options: column.options)
449
486
  record = html_options.delete(:object)
450
487
  options[:selected] = record.send(column.name)
451
488
  options[:object] = record
452
- options_for_select = active_scaffold_enum_options(column, record).collect do |text, value|
489
+ options_for_select = active_scaffold_enum_options(column, record, ui_options: ui_options).collect do |text, value|
453
490
  active_scaffold_translated_option(column, text, value)
454
491
  end
455
- html_options.merge!(column.options[:html_options] || {})
456
- options.merge!(column.options)
492
+ html_options.merge!(ui_options[:html_options] || {})
493
+ options.merge!(ui_options)
457
494
  active_scaffold_select_name_with_multiple html_options
458
495
  active_scaffold_translate_select_options(options)
459
- select(:record, column.name, options_for_select, options, html_options)
496
+ html = select(:record, column.name, options_for_select, options, html_options)
497
+ html << active_scaffold_refresh_link(column, html_options, record, ui_options) if ui_options[:refresh_link]
498
+ html
460
499
  end
461
500
 
462
- def active_scaffold_input_select(column, html_options)
501
+ def active_scaffold_input_select(column, html_options, ui_options: column.options)
463
502
  if column.association&.singular?
464
- active_scaffold_input_singular_association(column, html_options)
503
+ active_scaffold_input_singular_association(column, html_options, ui_options: ui_options)
465
504
  elsif column.association&.collection?
466
- active_scaffold_input_plural_association(column, html_options)
505
+ active_scaffold_input_plural_association(column, html_options, ui_options: ui_options)
467
506
  else
468
- active_scaffold_input_enum(column, html_options)
507
+ active_scaffold_input_enum(column, html_options, ui_options: ui_options)
469
508
  end
470
509
  end
471
510
 
472
- def active_scaffold_radio_option(option, selected, column, radio_options)
511
+ def active_scaffold_input_select_multiple(column, options, ui_options: column.options)
512
+ active_scaffold_input_select(column, options.merge(multiple: true), ui_options: ui_options)
513
+ end
514
+
515
+ def active_scaffold_radio_option(option, selected, column, radio_options, ui_options: column.options)
473
516
  if column.association
474
- label_method = column.options[:label_method] || :to_label
517
+ label_method = ui_options[:label_method] || :to_label
475
518
  text = option.send(label_method)
476
519
  value = option.id
477
520
  checked = {:checked => selected == value}
@@ -485,20 +528,20 @@ module ActiveScaffold
485
528
  content_tag(:label, radio_button(:record, column.name, value, radio_options) + text)
486
529
  end
487
530
 
488
- def active_scaffold_input_radio(column, html_options)
531
+ def active_scaffold_input_radio(column, html_options, ui_options: column.options)
489
532
  record = html_options[:object]
490
- html_options.merge!(column.options[:html_options] || {})
533
+ html_options.merge!(ui_options[:html_options] || {})
491
534
  options =
492
535
  if column.association
493
536
  sorted_association_options_find(column.association, nil, record)
494
537
  else
495
- active_scaffold_enum_options(column, record)
538
+ active_scaffold_enum_options(column, record, ui_options: ui_options)
496
539
  end
497
540
 
498
541
  selected = record.send(column.association.name) if column.association
499
542
  selected_id = selected&.id
500
543
  if options.present?
501
- if column.options[:add_new]
544
+ if ui_options[:add_new]
502
545
  html_options[:data] ||= {}
503
546
  html_options[:data][:subform_id] = active_scaffold_subform_attributes(column)[:id]
504
547
  radio_html_options = html_options.merge(class: html_options[:class] + ' hide-new-subform')
@@ -506,37 +549,37 @@ module ActiveScaffold
506
549
  radio_html_options = html_options
507
550
  end
508
551
  radios = options.map do |option|
509
- active_scaffold_radio_option(option, selected_id, column, radio_html_options)
552
+ active_scaffold_radio_option(option, selected_id, column, radio_html_options, ui_options: ui_options)
510
553
  end
511
- if column.options[:include_blank]
512
- label = column.options[:include_blank]
513
- label = as_(column.options[:include_blank]) if column.options[:include_blank].is_a?(Symbol)
554
+ if ui_options[:include_blank]
555
+ label = ui_options[:include_blank]
556
+ label = as_(ui_options[:include_blank]) if ui_options[:include_blank].is_a?(Symbol)
514
557
  radios.prepend content_tag(:label, radio_button(:record, column.name, '', html_options.merge(id: nil)) + label)
515
558
  end
516
- if column.options[:add_new]
517
- create_new_button = radio_button_tag(html_options[:name], '', selected&.new_record?, html_options.merge(id: nil, class: html_options[:class] + ' show-new-subform'))
559
+ if ui_options[:add_new]
560
+ create_new_button = radio_button_tag(html_options[:name], '', selected&.new_record?, html_options.merge(id: nil, class: html_options[:class] + ' show-new-subform').except(:object))
518
561
  radios << content_tag(:label, create_new_button << as_(:create_new)) <<
519
- active_scaffold_new_record_subform(column, record, html_options, skip_link: true)
562
+ active_scaffold_new_record_subform(column, record, html_options, ui_options: ui_options, skip_link: true)
520
563
  end
521
564
  safe_join radios
522
565
  else
523
566
  html = content_tag(:span, as_(:no_options), :class => "#{html_options[:class]} no-options", :id => html_options[:id])
524
567
  html << hidden_field_tag(html_options[:name], '', :id => nil)
525
- html << active_scaffold_new_record_subform(column, record, html_options) if column.options[:add_new]
568
+ html << active_scaffold_new_record_subform(column, record, html_options, ui_options: ui_options) if ui_options[:add_new]
526
569
  html
527
570
  end
528
571
  end
529
572
 
530
- def active_scaffold_input_checkbox(column, options)
531
- check_box(:record, column.name, options.merge(column.options))
573
+ def active_scaffold_input_checkbox(column, options, ui_options: column.options)
574
+ check_box(:record, column.name, options.merge(ui_options))
532
575
  end
533
576
 
534
- def active_scaffold_input_password(column, options)
535
- active_scaffold_text_input :password_field, column, options.reverse_merge(autocomplete: 'new-password')
577
+ def active_scaffold_input_password(column, options, ui_options: column.options)
578
+ active_scaffold_text_input :password_field, column, options.reverse_merge(autocomplete: 'new-password'), ui_options: ui_options
536
579
  end
537
580
 
538
- def active_scaffold_input_textarea(column, options)
539
- text_area(:record, column.name, options.merge(:cols => column.options[:cols], :rows => column.options[:rows], :size => column.options[:size]))
581
+ def active_scaffold_input_textarea(column, options, ui_options: column.options)
582
+ text_area(:record, column.name, options.merge(cols: ui_options[:cols], rows: ui_options[:rows], size: ui_options[:size]))
540
583
  end
541
584
 
542
585
  def active_scaffold_input_virtual(column, options)
@@ -547,55 +590,55 @@ module ActiveScaffold
547
590
  # Sadly, many of them lacks browser support
548
591
 
549
592
  # A text box, that accepts only valid email address (in-browser validation)
550
- def active_scaffold_input_email(column, options)
551
- active_scaffold_text_input :email_field, column, options
593
+ def active_scaffold_input_email(column, options, ui_options: column.options)
594
+ active_scaffold_text_input :email_field, column, options, ui_options: ui_options
552
595
  end
553
596
 
554
597
  # A text box, that accepts only valid URI (in-browser validation)
555
- def active_scaffold_input_url(column, options)
556
- active_scaffold_text_input :url_field, column, options
598
+ def active_scaffold_input_url(column, options, ui_options: column.options)
599
+ active_scaffold_text_input :url_field, column, options, ui_options: ui_options
557
600
  end
558
601
 
559
602
  # A text box, that accepts only valid phone-number (in-browser validation)
560
- def active_scaffold_input_telephone(column, options)
561
- active_scaffold_text_input :telephone_field, column, options, :format
603
+ def active_scaffold_input_telephone(column, options, ui_options: column.options)
604
+ active_scaffold_text_input :telephone_field, column, options, :format, ui_options: ui_options
562
605
  end
563
606
 
564
607
  # A spinbox control for number values (in-browser validation)
565
- def active_scaffold_input_number(column, options)
566
- active_scaffold_number_input :number_field, column, options, :format
608
+ def active_scaffold_input_number(column, options, ui_options: column.options)
609
+ active_scaffold_number_input :number_field, column, options, :format, ui_options: ui_options
567
610
  end
568
611
 
569
612
  # A slider control for number values (in-browser validation)
570
- def active_scaffold_input_range(column, options)
571
- active_scaffold_number_input :range_field, column, options, :format
613
+ def active_scaffold_input_range(column, options, ui_options: column.options)
614
+ active_scaffold_number_input :range_field, column, options, :format, ui_options: ui_options
572
615
  end
573
616
 
574
617
  # A slider control for number values (in-browser validation)
575
- def active_scaffold_number_input(method, column, options, remove_options = nil)
618
+ def active_scaffold_number_input(method, column, options, remove_options = nil, ui_options: column.options)
576
619
  options = numerical_constraints_for_column(column, options)
577
- active_scaffold_text_input method, column, options, remove_options
620
+ active_scaffold_text_input method, column, options, remove_options, ui_options: ui_options
578
621
  end
579
622
 
580
- def active_scaffold_text_input(method, column, options, remove_options = nil)
623
+ def active_scaffold_text_input(method, column, options, remove_options = nil, ui_options: column.options)
581
624
  options = active_scaffold_input_text_options(options)
582
- options = options.merge(column.options)
625
+ options = options.merge(ui_options)
583
626
  options = options.except(*remove_options) if remove_options.present?
584
627
  send method, :record, column.name, options
585
628
  end
586
629
 
587
630
  # A color picker
588
- def active_scaffold_input_color(column, options)
631
+ def active_scaffold_input_color(column, options, ui_options: column.options)
589
632
  html = []
590
633
  options = active_scaffold_input_text_options(options)
591
- if column.column&.null
634
+ if column.null?
592
635
  no_color = options[:object].send(column.name).nil?
593
636
  method = no_color ? :hidden_field : :color_field
594
- html << content_tag(:label, check_box_tag('disable', '1', no_color, id: nil, name: nil, class: 'no-color') << " #{as_ column.options[:no_color] || :no_color}")
637
+ html << content_tag(:label, check_box_tag('disable', '1', no_color, id: nil, name: nil, class: 'no-color') << " #{as_ ui_options[:no_color] || :no_color}")
595
638
  else
596
639
  method = :color_field
597
640
  end
598
- html << send(method, :record, column.name, options.merge(column.options).except(:format, :no_color))
641
+ html << send(method, :record, column.name, options.merge(ui_options).except(:format, :no_color))
599
642
  safe_join html
600
643
  end
601
644
 
@@ -603,34 +646,37 @@ module ActiveScaffold
603
646
  # Column.type-based inputs
604
647
  #
605
648
 
606
- def active_scaffold_input_boolean(column, options)
607
- record = options.delete(:object)
608
- select_options = []
609
- select_options << [as_(:_select_), nil] if !column.virtual? && column.column.null
610
- select_options << [as_(:true), true] # rubocop:disable Lint/BooleanSymbol
611
- select_options << [as_(:false), false] # rubocop:disable Lint/BooleanSymbol
649
+ def active_scaffold_input_boolean(column, html_options, ui_options: column.options)
650
+ record = html_options.delete(:object)
651
+ html_options.merge!(ui_options[:html_options] || {})
612
652
 
613
- select_tag(options[:name], options_for_select(select_options, record.send(column.name)), options)
653
+ options = {selected: record.send(column.name), object: record}
654
+ options[:include_blank] = :_select_ if column.null?
655
+ options.merge!(ui_options)
656
+ active_scaffold_translate_select_options(options)
657
+
658
+ options_for_select = [[as_(:true), true], [as_(:false), false]] # rubocop:disable Lint/BooleanSymbol
659
+ select(:record, column.name, options_for_select, options, html_options)
614
660
  end
615
661
 
616
- def active_scaffold_input_date(column, options)
617
- active_scaffold_text_input :date_field, column, options
662
+ def active_scaffold_input_date(column, options, ui_options: column.options)
663
+ active_scaffold_text_input :date_field, column, options, ui_options: ui_options
618
664
  end
619
665
 
620
- def active_scaffold_input_time(column, options)
621
- active_scaffold_text_input :time_field, column, options
666
+ def active_scaffold_input_time(column, options, ui_options: column.options)
667
+ active_scaffold_text_input :time_field, column, options, ui_options: ui_options
622
668
  end
623
669
 
624
- def active_scaffold_input_datetime(column, options)
625
- active_scaffold_text_input :datetime_local_field, column, options
670
+ def active_scaffold_input_datetime(column, options, ui_options: column.options)
671
+ active_scaffold_text_input :datetime_local_field, column, options, ui_options: ui_options
626
672
  end
627
673
 
628
- def active_scaffold_input_month(column, options)
629
- active_scaffold_text_input :month_field, column, options
674
+ def active_scaffold_input_month(column, options, ui_options: column.options)
675
+ active_scaffold_text_input :month_field, column, options, ui_options: ui_options
630
676
  end
631
677
 
632
- def active_scaffold_input_week(column, options)
633
- active_scaffold_text_input :week_field, column, options
678
+ def active_scaffold_input_week(column, options, ui_options: column.options)
679
+ active_scaffold_text_input :week_field, column, options, ui_options: ui_options
634
680
  end
635
681
 
636
682
  ##
@@ -669,8 +715,8 @@ module ActiveScaffold
669
715
  end
670
716
  alias override_input? override_input
671
717
 
672
- def subform_partial_for_column(column, klass = nil)
673
- subform_partial = "#{column.options[:layout] || active_scaffold_config_for(klass || column.association.klass).subform.layout}_subform"
718
+ def subform_partial_for_column(column, klass = nil, ui_options: column.options)
719
+ subform_partial = "#{ui_options[:layout] || active_scaffold_config_for(klass || column.association.klass).subform.layout}_subform"
674
720
  override_subform_partial(column, subform_partial) || subform_partial
675
721
  end
676
722
 
@@ -740,7 +786,7 @@ module ActiveScaffold
740
786
 
741
787
  # find minimum and maximum from validators
742
788
  # we can safely modify :min and :max by 1 for :greater_tnan or :less_than value only for integer values
743
- only_integer = column.column.type == :integer if column.column
789
+ only_integer = column.column_type == :integer if column.column
744
790
  only_integer ||= validators.find { |v| v.options[:only_integer] }.present?
745
791
  margin = only_integer ? 1 : 0
746
792
 
@@ -5,7 +5,7 @@ module ActiveScaffold
5
5
  def active_scaffold_human_condition_for(column)
6
6
  return if (value = field_search_params[column.name.to_s]).nil?
7
7
  search_ui = column.search_ui
8
- search_ui ||= column.column.type if column.column
8
+ search_ui ||= column.column_type if column.column
9
9
  if override_human_condition_column?(column)
10
10
  send(override_human_condition_column(column), value, {})
11
11
  elsif search_ui && override_human_condition?(search_ui)
@@ -26,6 +26,7 @@ module ActiveScaffold
26
26
  attribute = column.active_record_class.human_attribute_name(column.name)
27
27
  opt ||= :between if from && to
28
28
  opt ||= from ? '>=' : '<='
29
+ from = to = nil if opt&.in? %w[null not_null]
29
30
  "#{attribute} #{as_(opt).downcase} #{from} #{to}"
30
31
  end
31
32
 
@@ -37,28 +38,61 @@ module ActiveScaffold
37
38
  alias active_scaffold_human_condition_decimal active_scaffold_human_condition_integer
38
39
  alias active_scaffold_human_condition_float active_scaffold_human_condition_integer
39
40
 
40
- def active_scaffold_human_condition_string(column, value)
41
+ def active_scaffold_human_condition_range(column, value)
41
42
  opt = ActiveScaffold::Finder::STRING_COMPARATORS.key(value['opt']) || value['opt']
42
43
  to = "- #{value['to']}" if opt == 'BETWEEN'
43
44
  format_human_condition column, opt, "'#{value['from']}'", to
44
45
  end
46
+ alias active_scaffold_human_condition_string active_scaffold_human_condition_range
45
47
 
46
- def active_scaffold_human_condition_date(column, value)
47
- conversion = column.column.type == :date ? :to_date : :to_time
48
- from = controller.class.condition_value_for_datetime(column, value['from'], conversion)
49
- from = I18n.l from if from
50
- to = controller.class.condition_value_for_datetime(column, value['to'], conversion) if value['opt'] == 'BETWEEN' || (value['opt'].nil? && value['to'])
51
- to = "- #{I18n.l to}" if to
52
- format_human_condition column, value['opt'], from, to
48
+ def active_scaffold_human_condition_datetime(column, value)
49
+ case value['opt']
50
+ when 'RANGE'
51
+ range_type, range = value['range'].downcase.split('_')
52
+ format = active_scaffold_human_condition_datetime_range_format(range_type, range)
53
+ from, = controller.class.datetime_from_to(column, value)
54
+ "#{column.active_record_class.human_attribute_name(column.name)} = #{as_(value['range'].downcase).downcase} (#{I18n.l(from, :format => format)})"
55
+ when 'PAST', 'FUTURE'
56
+ from, to = controller.class.datetime_from_to(column, value)
57
+ "#{column.active_record_class.human_attribute_name(column.name)} #{as_('BETWEEN'.downcase).downcase} #{I18n.l(from)} - #{I18n.l(to)}"
58
+ else
59
+ from, to = controller.class.datetime_from_to(column, value)
60
+ "#{column.active_record_class.human_attribute_name(column.name)} #{as_(value['opt'].downcase).downcase} #{I18n.l(from)} #{value['opt'] == 'BETWEEN' ? '- ' + I18n.l(to) : ''}"
61
+ end
62
+ end
63
+ alias active_scaffold_human_condition_time active_scaffold_human_condition_datetime
64
+ alias active_scaffold_human_condition_date active_scaffold_human_condition_datetime
65
+ alias active_scaffold_human_condition_timestamp active_scaffold_human_condition_datetime
66
+
67
+ def active_scaffold_human_condition_datetime_range_format(range_type, range)
68
+ case range
69
+ when 'week'
70
+ first_day_of_week = I18n.translate 'active_scaffold.date_picker_options.firstDay'
71
+ if first_day_of_week == 1
72
+ '%W %Y'
73
+ else
74
+ '%U %Y'
75
+ end
76
+ when 'month'
77
+ '%b %Y'
78
+ when 'year'
79
+ '%Y'
80
+ else
81
+ I18n.translate 'date.formats.default'
82
+ end
53
83
  end
54
- alias active_scaffold_human_condition_time active_scaffold_human_condition_date
55
- alias active_scaffold_human_condition_datetime active_scaffold_human_condition_date
56
- alias active_scaffold_human_condition_timestamp active_scaffold_human_condition_date
84
+ # def active_scaffold_human_condition_date(column, value)
85
+ # conversion = column.column_type == :date ? :to_date : :to_time
86
+ # from = controller.class.condition_value_for_datetime(column, value['from'], conversion)
87
+ # from = I18n.l from if from
88
+ # to = controller.class.condition_value_for_datetime(column, value['to'], conversion) if value['opt'] == 'BETWEEN' || (value['opt'].nil? && value['to'])
89
+ # to = "- #{I18n.l to}" if to
90
+ # format_human_condition column, value['opt'], from, to
91
+ # end
57
92
 
58
93
  def active_scaffold_human_condition_boolean(column, value)
59
94
  attribute = column.active_record_class.human_attribute_name(column.name)
60
- label = as_(ActiveScaffold::Core.column_type_cast(value, column.column) ? :true : :false) # rubocop:disable Lint/BooleanSymbol
61
- as_(:boolean, :scope => :human_conditions, :column => attribute, :value => label)
95
+ as_(:boolean, :scope => :human_conditions, :column => attribute, :value => as_(value))
62
96
  end
63
97
  alias active_scaffold_human_condition_checkbox active_scaffold_human_condition_boolean
64
98