active_scaffold 3.6.20 → 3.7.1

Sign up to get free protection for your applications and to get access to all the features.
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