active_scaffold 4.1.6 → 4.2.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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +32 -0
  3. data/README.md +8 -6
  4. data/app/assets/javascripts/jquery/active_scaffold.js +103 -51
  5. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +35 -2
  6. data/app/assets/stylesheets/active_scaffold_images.scss +6 -0
  7. data/app/assets/stylesheets/{active_scaffold_layout.css → active_scaffold_layout.scss} +104 -4
  8. data/app/assets/stylesheets/tiny_mce_bridge.scss +11 -0
  9. data/app/views/active_scaffold_overrides/_base_form.html.erb +3 -2
  10. data/app/views/active_scaffold_overrides/_field_search.html.erb +2 -2
  11. data/app/views/active_scaffold_overrides/_form.html.erb +14 -4
  12. data/app/views/active_scaffold_overrides/_form_association.html.erb +1 -1
  13. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +5 -11
  14. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +1 -1
  15. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +1 -3
  16. data/app/views/active_scaffold_overrides/_new_record.js.erb +3 -1
  17. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +1 -1
  18. data/app/views/active_scaffold_overrides/_render_field.js.erb +67 -36
  19. data/app/views/active_scaffold_overrides/_update_field_on_create.js.erb +41 -6
  20. data/app/views/active_scaffold_overrides/action_links_menu.js.erb +1 -0
  21. data/config/locales/de.yml +9 -0
  22. data/config/locales/en.yml +11 -0
  23. data/config/locales/es.yml +8 -0
  24. data/config/locales/fr.yml +8 -0
  25. data/config/locales/hu.yml +8 -0
  26. data/config/locales/ja.yml +8 -0
  27. data/config/locales/ru.yml +8 -0
  28. data/lib/active_scaffold/actions/common_search.rb +2 -0
  29. data/lib/active_scaffold/actions/core.rb +47 -23
  30. data/lib/active_scaffold/actions/create.rb +2 -0
  31. data/lib/active_scaffold/actions/delete.rb +6 -0
  32. data/lib/active_scaffold/actions/field_search.rb +36 -11
  33. data/lib/active_scaffold/actions/list.rb +26 -8
  34. data/lib/active_scaffold/actions/mark.rb +6 -0
  35. data/lib/active_scaffold/actions/nested.rb +2 -0
  36. data/lib/active_scaffold/actions/search.rb +7 -0
  37. data/lib/active_scaffold/actions/show.rb +6 -0
  38. data/lib/active_scaffold/actions/subform.rb +2 -0
  39. data/lib/active_scaffold/actions/update.rb +8 -1
  40. data/lib/active_scaffold/active_record_permissions.rb +3 -3
  41. data/lib/active_scaffold/attribute_params.rb +35 -17
  42. data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +2 -0
  43. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +10 -9
  44. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +10 -3
  45. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +2 -0
  46. data/lib/active_scaffold/bridges/active_storage.rb +2 -0
  47. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +2 -0
  48. data/lib/active_scaffold/bridges/ancestry.rb +2 -0
  49. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +2 -0
  50. data/lib/active_scaffold/bridges/bitfields/list_ui.rb +2 -0
  51. data/lib/active_scaffold/bridges/bitfields.rb +2 -0
  52. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +9 -6
  53. data/lib/active_scaffold/bridges/cancan.rb +2 -0
  54. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +2 -0
  55. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge_helpers.rb +2 -0
  56. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +3 -1
  57. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -0
  58. data/lib/active_scaffold/bridges/carrierwave.rb +2 -0
  59. data/lib/active_scaffold/bridges/chosen/helpers.rb +13 -4
  60. data/lib/active_scaffold/bridges/chosen.rb +2 -0
  61. data/lib/active_scaffold/bridges/country_select/country_select_bridge_helper.rb +2 -0
  62. data/lib/active_scaffold/bridges/country_select.rb +2 -0
  63. data/lib/active_scaffold/bridges/date_picker/ext.rb +6 -0
  64. data/lib/active_scaffold/bridges/date_picker/helper.rb +7 -3
  65. data/lib/active_scaffold/bridges/date_picker.rb +2 -0
  66. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge.rb +2 -0
  67. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge_helpers.rb +2 -0
  68. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +3 -1
  69. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +2 -0
  70. data/lib/active_scaffold/bridges/dragonfly.rb +2 -0
  71. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +2 -0
  72. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +10 -9
  73. data/lib/active_scaffold/bridges/file_column/form_ui.rb +2 -0
  74. data/lib/active_scaffold/bridges/file_column/list_ui.rb +2 -0
  75. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +2 -0
  76. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +2 -0
  77. data/lib/active_scaffold/bridges/file_column.rb +2 -0
  78. data/lib/active_scaffold/bridges/logical_query_parser/tokens_grammar.rb +65 -0
  79. data/lib/active_scaffold/bridges/logical_query_parser/tokens_grammar.treetop +31 -0
  80. data/lib/active_scaffold/bridges/logical_query_parser.rb +9 -0
  81. data/lib/active_scaffold/bridges/paper_trail/actions.rb +2 -0
  82. data/lib/active_scaffold/bridges/paper_trail/config.rb +2 -0
  83. data/lib/active_scaffold/bridges/paper_trail/helper.rb +2 -0
  84. data/lib/active_scaffold/bridges/paper_trail/paper_trail_bridge.rb +2 -0
  85. data/lib/active_scaffold/bridges/paper_trail.rb +2 -0
  86. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +3 -1
  87. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +2 -0
  88. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +2 -0
  89. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +12 -12
  90. data/lib/active_scaffold/bridges/paperclip.rb +2 -0
  91. data/lib/active_scaffold/bridges/record_select/helpers.rb +19 -11
  92. data/lib/active_scaffold/bridges/record_select.rb +2 -0
  93. data/lib/active_scaffold/bridges/semantic_attributes/column.rb +2 -0
  94. data/lib/active_scaffold/bridges/semantic_attributes.rb +2 -0
  95. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
  96. data/lib/active_scaffold/bridges/tiny_mce.rb +6 -0
  97. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +2 -0
  98. data/lib/active_scaffold/bridges/usa_state_select.rb +2 -0
  99. data/lib/active_scaffold/bridges.rb +2 -0
  100. data/lib/active_scaffold/config/base.rb +12 -7
  101. data/lib/active_scaffold/config/core.rb +26 -23
  102. data/lib/active_scaffold/config/create.rb +2 -0
  103. data/lib/active_scaffold/config/delete.rb +2 -0
  104. data/lib/active_scaffold/config/field_search.rb +2 -0
  105. data/lib/active_scaffold/config/form.rb +11 -1
  106. data/lib/active_scaffold/config/list.rb +7 -7
  107. data/lib/active_scaffold/config/mark.rb +2 -0
  108. data/lib/active_scaffold/config/nested.rb +28 -0
  109. data/lib/active_scaffold/config/search.rb +2 -0
  110. data/lib/active_scaffold/config/show.rb +2 -0
  111. data/lib/active_scaffold/config/subform.rb +2 -0
  112. data/lib/active_scaffold/config/update.rb +3 -1
  113. data/lib/active_scaffold/configurable.rb +4 -2
  114. data/lib/active_scaffold/constraints.rb +2 -0
  115. data/lib/active_scaffold/core.rb +14 -4
  116. data/lib/active_scaffold/data_structures/action_columns.rb +3 -1
  117. data/lib/active_scaffold/data_structures/action_link.rb +10 -0
  118. data/lib/active_scaffold/data_structures/action_link_separator.rb +2 -0
  119. data/lib/active_scaffold/data_structures/action_links.rb +32 -21
  120. data/lib/active_scaffold/data_structures/actions.rb +4 -2
  121. data/lib/active_scaffold/data_structures/association/abstract.rb +4 -2
  122. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +4 -2
  123. data/lib/active_scaffold/data_structures/association/active_record.rb +3 -9
  124. data/lib/active_scaffold/data_structures/association/mongoid.rb +4 -2
  125. data/lib/active_scaffold/data_structures/association.rb +2 -0
  126. data/lib/active_scaffold/data_structures/bridge.rb +3 -1
  127. data/lib/active_scaffold/data_structures/column.rb +37 -3
  128. data/lib/active_scaffold/data_structures/columns.rb +4 -2
  129. data/lib/active_scaffold/data_structures/filter.rb +3 -3
  130. data/lib/active_scaffold/data_structures/filter_option.rb +2 -0
  131. data/lib/active_scaffold/data_structures/filters.rb +3 -3
  132. data/lib/active_scaffold/data_structures/nested_info.rb +4 -2
  133. data/lib/active_scaffold/data_structures/set.rb +8 -10
  134. data/lib/active_scaffold/data_structures/sorting.rb +5 -7
  135. data/lib/active_scaffold/engine.rb +3 -4
  136. data/lib/active_scaffold/extensions/action_controller_rendering.rb +2 -0
  137. data/lib/active_scaffold/extensions/action_controller_rescueing.rb +2 -0
  138. data/lib/active_scaffold/extensions/action_view_rendering.rb +2 -0
  139. data/lib/active_scaffold/extensions/connection_adapter.rb +2 -0
  140. data/lib/active_scaffold/extensions/ice_nine.rb +2 -0
  141. data/lib/active_scaffold/extensions/localize.rb +2 -0
  142. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +2 -0
  143. data/lib/active_scaffold/extensions/paginator_extensions.rb +3 -1
  144. data/lib/active_scaffold/extensions/routing_mapper.rb +2 -0
  145. data/lib/active_scaffold/extensions/to_label.rb +2 -0
  146. data/lib/active_scaffold/extensions/unsaved_associated.rb +10 -8
  147. data/lib/active_scaffold/extensions/unsaved_record.rb +2 -0
  148. data/lib/active_scaffold/finder.rb +57 -18
  149. data/lib/active_scaffold/helpers/action_link_helpers.rb +112 -37
  150. data/lib/active_scaffold/helpers/association_helpers.rb +4 -2
  151. data/lib/active_scaffold/helpers/controller_helpers.rb +2 -0
  152. data/lib/active_scaffold/helpers/filter_helpers.rb +11 -3
  153. data/lib/active_scaffold/helpers/form_column_helpers.rb +145 -110
  154. data/lib/active_scaffold/helpers/human_condition_helpers.rb +2 -0
  155. data/lib/active_scaffold/helpers/id_helpers.rb +2 -0
  156. data/lib/active_scaffold/helpers/list_column_helpers.rb +9 -5
  157. data/lib/active_scaffold/helpers/pagination_helpers.rb +3 -1
  158. data/lib/active_scaffold/helpers/search_column_helpers.rb +19 -9
  159. data/lib/active_scaffold/helpers/show_column_helpers.rb +4 -2
  160. data/lib/active_scaffold/helpers/tabs_helpers.rb +5 -3
  161. data/lib/active_scaffold/helpers/view_helpers.rb +3 -3
  162. data/lib/active_scaffold/marked_model.rb +6 -5
  163. data/lib/active_scaffold/orm_checks.rb +2 -0
  164. data/lib/active_scaffold/paginator.rb +4 -1
  165. data/lib/active_scaffold/registry.rb +2 -0
  166. data/lib/active_scaffold/responds_to_parent.rb +2 -0
  167. data/lib/active_scaffold/tableless.rb +23 -13
  168. data/lib/active_scaffold/version.rb +4 -2
  169. data/lib/active_scaffold.rb +10 -2
  170. data/lib/generators/active_scaffold/controller/USAGE +19 -0
  171. data/lib/generators/active_scaffold/controller/controller_generator.rb +29 -0
  172. data/lib/generators/active_scaffold/install/USAGE +2 -0
  173. data/lib/generators/active_scaffold/{install_generator.rb → install/install_generator.rb} +10 -6
  174. data/lib/generators/active_scaffold/resource/USAGE +29 -0
  175. data/lib/generators/active_scaffold/resource/resource_generator.rb +30 -0
  176. data/lib/tasks/brakeman.rake +2 -0
  177. data/shoulda_macros/macros.rb +2 -0
  178. metadata +19 -11
  179. data/lib/generators/active_scaffold/controller_generator.rb +0 -49
  180. data/lib/generators/active_scaffold/resource_generator.rb +0 -56
  181. /data/lib/generators/{templates → active_scaffold/controller/templates}/controller.rb +0 -0
  182. /data/lib/generators/{templates → active_scaffold/controller/templates}/helper.rb +0 -0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  # Helpers that assist with the rendering of a Form Column
@@ -23,7 +25,7 @@ module ActiveScaffold
23
25
 
24
26
  elsif column.association
25
27
  # if we get here, it's because the column has a form_ui but not one ActiveScaffold knows about.
26
- raise "Unknown form_ui `#{column.form_ui}' for column `#{column.name}'" if column.form_ui
28
+ raise ArgumentError, "Unknown form_ui `#{column.form_ui}' for column `#{column.name}'" if column.form_ui
27
29
 
28
30
  # its an association and nothing is specified, we will assume form_ui :select
29
31
  active_scaffold_input_select(column, options)
@@ -47,14 +49,15 @@ module ActiveScaffold
47
49
  text_field(:record, column.name, options.merge(column.options).except(:format))
48
50
  end
49
51
  rescue StandardError => e
50
- Rails.logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
51
- raise e
52
+ message = "on the ActiveScaffold column = :#{column.name} in #{controller.class}"
53
+ ActiveScaffold.log_exception(e, message)
54
+ raise e.class, "#{e.message} -- #{message}", e.backtrace
52
55
  end
53
56
 
54
57
  def active_scaffold_render_subform_column(column, scope, crud_type, readonly, add_class = false, record = nil) # rubocop:disable Metrics/ParameterLists
55
58
  if add_class
56
59
  col_class = []
57
- col_class << 'required' if column.required?(action_for_validation?(record))
60
+ col_class << 'required' if column.required?(action_for_validation(record))
58
61
  col_class << column.css_class unless column.css_class.nil? || column.css_class.is_a?(Proc)
59
62
  col_class << 'hidden' if column_renders_as(column) == :hidden
60
63
  col_class << 'checkbox' if column.form_ui == :checkbox
@@ -77,6 +80,18 @@ module ActiveScaffold
77
80
  }
78
81
  end
79
82
 
83
+ def active_scaffold_subform_record_actions(association_column, record, locked, scope)
84
+ return unless association_column.association.collection? && !locked
85
+
86
+ auth = %i[destroy delete_all delete].exclude?(association_column.association.dependent)
87
+ auth, reason = record.authorized_for?(crud_type: :delete, reason: true) unless auth
88
+ if auth
89
+ link_to(as_(:remove), '#', class: 'destroy')
90
+ else
91
+ content_tag :span, reason, class: 'destroy reason'
92
+ end
93
+ end
94
+
80
95
  # the standard active scaffold options used for textual inputs
81
96
  def active_scaffold_input_text_options(options = {})
82
97
  options[:autocomplete] ||= 'off'
@@ -84,7 +99,7 @@ module ActiveScaffold
84
99
  options
85
100
  end
86
101
 
87
- def action_for_validation?(record)
102
+ def action_for_validation(record)
88
103
  record&.persisted? ? :update : :create
89
104
  end
90
105
 
@@ -94,7 +109,7 @@ module ActiveScaffold
94
109
  record = options[:object]
95
110
 
96
111
  # Add some HTML5 attributes for in-browser validation and better user experience
97
- if column.required?(action_for_validation?(record)) && (!@disable_required_for_new || scope.nil? || record&.persisted?)
112
+ if column.required?(action_for_validation(record)) && (!@disable_required_for_new || scope.nil? || record&.persisted?)
98
113
  options[:required] = true
99
114
  end
100
115
  options[:placeholder] = column.placeholder if column.placeholder.present?
@@ -112,7 +127,9 @@ module ActiveScaffold
112
127
  end
113
128
 
114
129
  def current_form_columns(record, scope, subform_controller = nil)
115
- if scope
130
+ if @main_columns && (scope.nil? || subform_controller == controller.class)
131
+ @main_columns.visible_columns_names
132
+ elsif scope
116
133
  subform_controller.active_scaffold_config.subform.columns.visible_columns_names
117
134
  elsif %i[new create edit update render_field].include? action_name.to_sym
118
135
  # disable update_columns for inplace_edit (GET render_field)
@@ -122,29 +139,33 @@ module ActiveScaffold
122
139
  end
123
140
  end
124
141
 
142
+ def url_options_for_render_field(column, record, scope, subform_controller, url_params)
143
+ url_params.reverse_merge! params_for(action: 'render_field', column: column.name, id: record.to_param)
144
+ if nested? && scope
145
+ url_params[:nested] = url_params.slice(:parent_scaffold, :association, nested.param_name)
146
+ url_params = url_params.except(:parent_scaffold, :association, nested.param_name)
147
+ end
148
+ if scope
149
+ url_params[:parent_controller] ||= url_params[:controller].gsub(%r{^/}, '')
150
+ url_params[:controller] = subform_controller.controller_path
151
+ url_params[:scope] = scope
152
+ url_params[:parent_id] = params[:parent_id] || params[:id]
153
+ end
154
+ url_params
155
+ end
156
+
125
157
  def update_columns_options(column, scope, options, force = false, form_columns: nil, url_params: {})
126
158
  record = options[:object]
127
159
  subform_controller = controller.class.active_scaffold_controller_for(record.class) if scope
128
- if @main_columns && (scope.nil? || subform_controller == controller.class)
129
- form_columns ||= @main_columns.visible_columns_names
130
- end
131
160
  form_columns ||= current_form_columns(record, scope, subform_controller)
132
- if force || (form_columns && column.update_columns&.intersect?(form_columns))
133
- url_params.reverse_merge! params_for(action: 'render_field', column: column.name, id: record.to_param)
134
- if nested? && scope
135
- url_params[:nested] = url_params.slice(:parent_scaffold, :association, nested.param_name)
136
- url_params = url_params.except(:parent_scaffold, :association, nested.param_name)
137
- end
138
- if scope
139
- url_params[:parent_controller] ||= url_params[:controller].gsub(%r{^/}, '')
140
- url_params[:controller] = subform_controller.controller_path
141
- url_params[:scope] = scope
142
- url_params[:parent_id] = params[:parent_id] || params[:id]
143
- end
161
+ update_columns = column.update_columns&.flat_map { |col| col.is_a?(Hash) ? col.keys : col }
162
+ force = true if update_columns&.include?(:__root__)
144
163
 
164
+ if force || (form_columns && update_columns&.intersect?(form_columns))
165
+ url_params = url_options_for_render_field(column, record, scope, subform_controller, url_params)
145
166
  options[:class] = "#{options[:class]} update_form".strip
146
167
  options['data-update_url'] = url_for(url_params)
147
- options['data-update_send_form'] = column.send_form_on_update_column
168
+ options['data-update_send_form'] = column.update_columns&.any?(Hash) || column.send_form_on_update_column
148
169
  options['data-update_send_form_selector'] = column.options[:send_form_selector] if column.options[:send_form_selector]
149
170
  options['data-skip-disable-form'] = !column.disable_on_update_column
150
171
  end
@@ -203,13 +224,18 @@ module ActiveScaffold
203
224
  end
204
225
  end
205
226
 
227
+ def column_description(column, record, scope = nil)
228
+ desc = column.description(record, scope)
229
+ content_tag(:span, h(desc) + content_tag(:span, nil, class: 'close'), class: 'description') if desc.present?
230
+ end
231
+
206
232
  def form_attribute(column, record, scope = nil, only_value = false, col_class = nil)
207
233
  column_options = active_scaffold_input_options(column, scope, object: record)
208
234
  collapsible_id = column_options.delete :collapsible_id
209
235
  attributes = field_attributes(column, record)
210
236
  attributes[:class] = "#{attributes[:class]} #{col_class}" if col_class.present?
211
237
  if only_value
212
- field = content_tag(:span, get_column_value(record, column), column_options.except(:name, :object))
238
+ field = content_tag(:span, show_column_value(record, column), column_options.except(:name, :object))
213
239
  if column.association.nil? || column.association.belongs_to?
214
240
  # hidden field probably not needed, but leaving it just in case
215
241
  # but it isn't working for assocations which are not belongs_to
@@ -221,8 +247,8 @@ module ActiveScaffold
221
247
  end
222
248
  if field
223
249
  field << loading_indicator_tag(action: :render_field, id: params[:id]) if column.update_columns
224
- desc = column.description(record, scope)
225
- field << content_tag(:span, desc, class: 'description') if desc.present?
250
+ desc = column_description(column, record, scope)
251
+ field << desc if desc.present?
226
252
  end
227
253
 
228
254
  label = label_tag(label_for(column, column_options), form_column_label(column, record, scope))
@@ -352,7 +378,6 @@ module ActiveScaffold
352
378
  collection_select(:record, method, select_options, :id, ui_options[:label_method] || :to_label, options, html_options)
353
379
  end
354
380
  html << active_scaffold_refresh_link(column, html_options, record, ui_options) if ui_options[:refresh_link]
355
- html << active_scaffold_add_new(column, record, html_options, ui_options: ui_options) if ui_options[:add_new]
356
381
  html
357
382
  end
358
383
 
@@ -368,19 +393,7 @@ module ActiveScaffold
368
393
 
369
394
  def active_scaffold_add_new(column, record, html_options, ui_options: column.options, skip_link: false)
370
395
  options = ui_options[:add_new] == true ? {} : ui_options[:add_new]
371
- if options.is_a?(Array)
372
- ActiveScaffold.deprecator.warn "use add_new: {types: #{options.inspect}} instead of add_new: #{options.inspect}"
373
- options = {types: options}
374
- end
375
- if ui_options[:hide_subgroups] && !options.key?(:hide_subgroups)
376
- ActiveScaffold.deprecator.warn "use add_new: {hide_subgroups: #{ui_options[:hide_subgroups]}} instead of hide_subgroups: #{ui_options[:hide_subgroups]}"
377
- options[:hide_subgroups] = ui_options[:hide_subgroups]
378
- end
379
- if ui_options[:layout] && !options.key?(:layout)
380
- ActiveScaffold.deprecator.warn "use add_new: {layout: #{ui_options[:layout]}} instead of layout: #{ui_options[:layout]}"
381
- options[:layout] = ui_options[:layout]
382
- end
383
-
396
+ options[:mode] = :popup if column.association&.collection?
384
397
  case options[:mode]
385
398
  when nil, :subform
386
399
  active_scaffold_new_record_subform(column, record, html_options, options: options, skip_link: skip_link)
@@ -461,36 +474,37 @@ module ActiveScaffold
461
474
  link_to(label, '#', data: data, class: 'show-new-subform')
462
475
  end
463
476
 
464
- def active_scaffold_file_with_remove_link(column, options, content, remove_file_prefix, controls_class, ui_options: column.options, &block)
477
+ def active_scaffold_file_with_content(column, options, content, remove_file_prefix, controls_class, ui_options: column.options)
465
478
  options = active_scaffold_input_text_options(options.merge(ui_options))
479
+ options[:style] = 'display: none' if content
480
+ field = file_field(:record, column.name, options)
481
+
466
482
  if content
467
- active_scaffold_file_with_content(column, content, options, remove_file_prefix, controls_class, &block)
483
+ content = [content, ' | ']
484
+ content << yield if block_given?
485
+ object_name, method = options[:name].split(/\[(#{column.name})\]/)
486
+ method.sub!(/#{column.name}/, "#{remove_file_prefix}\\0")
487
+ content << hidden_field(object_name, method, value: 'false', class: 'remove_file')
488
+ active_scaffold_file_with_remove_link(safe_join(content), options, controls_class) { field }
468
489
  else
469
- file_field(:record, column.name, options)
490
+ field
470
491
  end
471
492
  end
472
493
 
473
- def active_scaffold_file_with_content(column, content, options, remove_file_prefix, controls_class)
494
+ def active_scaffold_file_with_remove_link(content, options, controls_class, link_key = nil)
474
495
  required = options.delete(:required)
475
- js_remove_file_code = "jQuery(this).prev().val('true'); jQuery(this).parent().hide().next().show()#{".find('input').attr('required', 'required')" if required}; return false;"
476
- js_dont_remove_file_code = "jQuery(this).parents('div.#{controls_class}').find('input.remove_file').val('false'); return false;"
477
-
478
- object_name, method = options[:name].split(/\[(#{column.name})\]/)
479
- method.sub!(/#{column.name}/, "#{remove_file_prefix}\\0")
480
- fields = block_given? ? yield : ''
481
- link_key = options[:multiple] ? :remove_files : :remove_file
482
- input = file_field(:record, column.name, options.merge(onchange: js_dont_remove_file_code))
483
- content_tag(:div, class: controls_class) do
484
- content_tag(:div) do
485
- safe_join [content, ' | ', fields,
486
- hidden_field(object_name, method, value: 'false', class: 'remove_file'),
487
- content_tag(:a, as_(link_key), href: '#', onclick: js_remove_file_code)]
488
- end << content_tag(:div, input, style: 'display: none')
496
+ link_key ||= options[:multiple] ? :remove_files : :remove_file
497
+ content_tag(:div, class: "#{controls_class} file-input-controls", data: {required: required}) do
498
+ content_line = content_tag(:div) do
499
+ safe_join [content, content_tag(:a, as_(link_key), href: '#', class: 'remove-file-btn')]
500
+ end
501
+ content_line << yield if block_given?
502
+ content_line
489
503
  end
490
504
  end
491
505
 
492
506
  def active_scaffold_refresh_link(column, html_options, record, ui_options = {})
493
- link_options = {object: record}
507
+ link_options = {object: record, data: {field_selector: ui_options[:field_selector] || "##{html_options[:id]}"}}
494
508
  if html_options['data-update_url']
495
509
  link_options['data-update_send_form'] = html_options['data-update_send_form']
496
510
  link_options['data-update_send_form_selector'] = html_options['data-update_send_form_selector']
@@ -533,7 +547,7 @@ module ActiveScaffold
533
547
  end
534
548
 
535
549
  def active_scaffold_input_draggable(column, options, ui_options: column.options)
536
- active_scaffold_input_plural_association(column, options.merge(draggable_lists: true), ui_options: ui_options)
550
+ active_scaffold_input_select(column, options.merge(draggable_lists: true), ui_options: ui_options)
537
551
  end
538
552
 
539
553
  def active_scaffold_checkbox_option(option, label_method, associated_ids, checkbox_options, li_options = {})
@@ -594,13 +608,20 @@ module ActiveScaffold
594
608
  end
595
609
 
596
610
  def active_scaffold_input_select(column, html_options, ui_options: column.options)
597
- if column.association&.singular?
598
- active_scaffold_input_singular_association(column, html_options, ui_options: ui_options)
599
- elsif column.association&.collection?
600
- active_scaffold_input_plural_association(column, html_options, ui_options: ui_options)
601
- else
602
- active_scaffold_input_enum(column, html_options, ui_options: ui_options)
611
+ record = html_options[:object]
612
+ html =
613
+ if column.association&.singular?
614
+ active_scaffold_input_singular_association(column, html_options, ui_options: ui_options)
615
+ elsif column.association&.collection?
616
+ active_scaffold_input_plural_association(column, html_options, ui_options: ui_options)
617
+ else
618
+ active_scaffold_input_enum(column, html_options, ui_options: ui_options)
619
+ end
620
+ if ui_options[:add_new]
621
+ html = content_tag(:div, html, class: 'select-field') <<
622
+ active_scaffold_add_new(column, record, html_options, ui_options: ui_options)
603
623
  end
624
+ html
604
625
  end
605
626
 
606
627
  def active_scaffold_input_select_multiple(column, options, ui_options: column.options)
@@ -623,6 +644,54 @@ module ActiveScaffold
623
644
  content_tag(:label, radio_button(:record, column.name, value, radio_options) + text)
624
645
  end
625
646
 
647
+ def active_scaffold_input_radio_content(column, record, options, html_options, ui_options)
648
+ if ui_options[:add_new]
649
+ add_new_subform = ui_options[:add_new] == true || ui_options[:add_new][:mode].in?([nil, :subform])
650
+ if add_new_subform
651
+ html_options[:data] ||= {}
652
+ html_options[:data][:subform_id] = active_scaffold_subform_attributes(column, ui_options: ui_options)[:id]
653
+ end
654
+ radio_html_options = html_options.merge(class: "#{html_options[:class]} hide-new-subform")
655
+ else
656
+ radio_html_options = html_options
657
+ end
658
+
659
+ selected = record.send(column.association.name) if column.association
660
+ radios = options.map do |option|
661
+ active_scaffold_radio_option(option, selected&.id, column, radio_html_options, ui_options: ui_options)
662
+ end
663
+
664
+ if ui_options[:include_blank]
665
+ label = ui_options[:include_blank]
666
+ label = as_(ui_options[:include_blank]) if ui_options[:include_blank].is_a?(Symbol)
667
+ radio_id = "#{html_options[:id]}-"
668
+ radios.prepend content_tag(:label, radio_button(:record, column.name, '', html_options.merge(id: radio_id)) + label)
669
+ end
670
+ if ui_options[:add_new]
671
+ if add_new_subform
672
+ create_new = content_tag(:label) do
673
+ radio_button_tag(html_options[:name], '', selected&.new_record?, html_options.merge(
674
+ id: "#{html_options[:id]}-create_new", class: "#{html_options[:class]} show-new-subform"
675
+ ).except(:object)) <<
676
+ active_scaffold_add_new_text(ui_options[:add_new], :add_new_text, :create_new)
677
+ end
678
+ radios << create_new
679
+ skip_link = true
680
+ else
681
+ ui_options = ui_options.merge(add_new: ui_options[:add_new].merge(
682
+ url_options: {
683
+ parent_scope: html_options[:name].gsub(/^record|\[[^\]]*\]$/, '').presence,
684
+ radio_data: html_options.slice(*html_options.keys.grep(/^data-update_/))
685
+ }
686
+ ))
687
+ radios << content_tag(:span, '', class: 'new-radio-container', id: html_options[:id])
688
+ end
689
+ radios << active_scaffold_add_new(column, record, html_options, ui_options: ui_options, skip_link: skip_link)
690
+ end
691
+
692
+ safe_join radios
693
+ end
694
+
626
695
  def active_scaffold_input_radio(column, html_options, ui_options: column.options)
627
696
  record = html_options[:object]
628
697
  html_options.merge!(ui_options[:html_options] || {})
@@ -635,53 +704,19 @@ module ActiveScaffold
635
704
  send(enum_options_method, column, record, ui_options: ui_options)
636
705
  end
637
706
 
638
- selected = record.send(column.association.name) if column.association
639
- selected_id = selected&.id
640
707
  if options.present?
708
+ html = active_scaffold_input_radio_content(column, record, options, html_options, ui_options)
709
+ else
710
+ html = content_tag(:span, as_(:no_options), class: "#{html_options[:class]} no-options")
711
+ html << hidden_field_tag(html_options[:name], '', id: html_options[:id])
641
712
  if ui_options[:add_new]
642
- html_options[:data] ||= {}
643
- html_options[:data][:subform_id] = active_scaffold_subform_attributes(column, ui_options: ui_options)[:id]
644
- radio_html_options = html_options.merge(class: "#{html_options[:class]} hide-new-subform")
645
- else
646
- radio_html_options = html_options
647
- end
648
- radios = options.map do |option|
649
- active_scaffold_radio_option(option, selected_id, column, radio_html_options, ui_options: ui_options)
650
- end
651
- if ui_options[:include_blank]
652
- label = ui_options[:include_blank]
653
- label = as_(ui_options[:include_blank]) if ui_options[:include_blank].is_a?(Symbol)
654
- radio_id = "#{html_options[:id]}-"
655
- radios.prepend content_tag(:label, radio_button(:record, column.name, '', html_options.merge(id: radio_id)) + label)
656
- end
657
- if ui_options[:add_new]
658
- if ui_options[:add_new] == true || ui_options[:add_new][:mode].in?([nil, :subform])
659
- create_new = content_tag(:label) do
660
- radio_button_tag(html_options[:name], '', selected&.new_record?, html_options.merge(
661
- id: "#{html_options[:id]}-create_new", class: "#{html_options[:class]} show-new-subform"
662
- ).except(:object)) <<
663
- active_scaffold_add_new_text(ui_options[:add_new], :add_new_text, :create_new)
664
- end
665
- radios << create_new
666
- skip_link = true
667
- else
668
- ui_options = ui_options.merge(add_new: ui_options[:add_new].merge(
669
- url_options: {
670
- parent_scope: html_options[:name].gsub(/^record|\[[^\]]*\]$/, '').presence,
671
- radio_data: html_options.slice(*html_options.keys.grep(/^data-update_/))
672
- }
673
- ))
674
- radios << content_tag(:span, '', class: 'new-radio-container', id: html_options[:id])
675
- end
676
- radios << active_scaffold_add_new(column, record, html_options, ui_options: ui_options, skip_link: skip_link)
713
+ html = content_tag(:div, html, class: 'select-field') <<
714
+ active_scaffold_add_new(column, record, html_options, ui_options: ui_options)
677
715
  end
678
- safe_join radios
679
- else
680
- html = content_tag(:span, as_(:no_options), class: "#{html_options[:class]} no-options", id: html_options[:id])
681
- html << hidden_field_tag(html_options[:name], '', id: nil)
682
- html << active_scaffold_add_new(column, record, html_options, ui_options: ui_options) if ui_options[:add_new]
683
716
  html
684
717
  end
718
+ html << active_scaffold_refresh_link(column, html_options, record, ui_options.merge(field_selector: "[name=\"#{html_options[:name]}\"]")) if ui_options[:refresh_link]
719
+ html
685
720
  end
686
721
 
687
722
  def active_scaffold_input_checkbox(column, options, ui_options: column.options)
@@ -851,9 +886,9 @@ module ActiveScaffold
851
886
  end
852
887
  end
853
888
 
854
- def column_scope(column, scope = nil, record = nil)
889
+ def column_scope(column, scope = nil, record = nil, generated_id = nil)
855
890
  if column.association&.collection?
856
- "#{scope}[#{column.name}][#{record.id || generate_temporary_id(record)}]"
891
+ "#{scope}[#{column.name}][#{record.id || generate_temporary_id(record, generated_id)}]"
857
892
  else
858
893
  "#{scope}[#{column.name}]"
859
894
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  # Helpers that assist with rendering of a human readable search statement
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  # A bunch of helper methods to produce the common view ids
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  # Helpers that assist with the rendering of a List Column
@@ -22,8 +24,9 @@ module ActiveScaffold
22
24
  value = '&nbsp;'.html_safe if value.nil? || value.blank? # fix for IE 6
23
25
  value
24
26
  rescue StandardError => e
25
- Rails.logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}, record: #{record.inspect}"
26
- raise e
27
+ message = "on the ActiveScaffold column = :#{column.name} in #{controller.class}, record: #{record.inspect}"
28
+ ActiveScaffold.log_exception(e, message)
29
+ raise e.class, "#{e.message} -- #{message}", e.backtrace
27
30
  end
28
31
 
29
32
  def get_column_method(record, column)
@@ -62,8 +65,9 @@ module ActiveScaffold
62
65
  text
63
66
  end
64
67
  rescue StandardError => e
65
- Rails.logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
66
- raise e
68
+ message = "on the ActiveScaffold column = :#{column.name} in #{controller.class}"
69
+ ActiveScaffold.log_exception(e, message)
70
+ raise e.class, "#{e.message} -- #{message}", e.backtrace
67
71
  end
68
72
 
69
73
  def column_wrap_tag
@@ -303,7 +307,7 @@ module ActiveScaffold
303
307
  empty_field_text(column)
304
308
  elsif column_value.is_a?(Time) || column_value.is_a?(Date)
305
309
  l(column_value, format: options&.dig(:format) || :default)
306
- elsif !!column_value == column_value # rubocop:disable Style/DoubleNegation fast check for boolean
310
+ elsif !!column_value == column_value # rubocop:disable Style/DoubleNegation -- fast check for boolean
307
311
  as_(column_value.to_s.to_sym)
308
312
  else
309
313
  column_value.to_s
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  module PaginationHelpers
@@ -27,7 +29,7 @@ module ActiveScaffold
27
29
 
28
30
  html = []
29
31
  if current_page.number == 1
30
- last_page = 0 # rubocop:disable Lint/UselessAssignment
32
+ last_page = 0
31
33
  else
32
34
  last_page = 1
33
35
  last_page.upto([last_page + outer_window, current_page.number - 1].min) do |num|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  # Helpers that assist with the rendering of a Form Column
@@ -43,8 +45,9 @@ module ActiveScaffold
43
45
  text_field(:record, column.name, options.merge(column.options))
44
46
  end
45
47
  rescue StandardError => e
46
- Rails.logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
47
- raise e
48
+ message = "on the ActiveScaffold column = :#{column.name} in #{controller.class}"
49
+ ActiveScaffold.log_exception(e, message)
50
+ raise e.class, "#{e.message} -- #{message}", e.backtrace
48
51
  end
49
52
 
50
53
  # the standard active scaffold options used for class, name and scope
@@ -205,13 +208,20 @@ module ActiveScaffold
205
208
  end
206
209
 
207
210
  def active_scaffold_search_range_comparator_options(column, ui_options: column.options)
208
- select_options = ActiveScaffold::Finder::NUMERIC_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] }
211
+ select_options = []
209
212
  if active_scaffold_search_range_string?(column)
210
- comparators = ActiveScaffold::Finder::STRING_COMPARATORS.collect { |title, comp| [as_(title), comp] }
211
- select_options.unshift(*comparators)
213
+ if column.search_sql.present?
214
+ select_options.concat(ActiveScaffold::Finder::STRING_COMPARATORS.collect { |title, comp| [as_(title), comp] })
215
+ end
216
+ if ActiveScaffold::Finder::LOGICAL_COMPARATORS.present? && column.logical_search.present?
217
+ select_options.concat(ActiveScaffold::Finder::LOGICAL_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] })
218
+ end
212
219
  end
213
- if include_null_comparators? column, ui_options: ui_options
214
- select_options.concat(ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] })
220
+ if column.search_sql.present?
221
+ select_options.concat(ActiveScaffold::Finder::NUMERIC_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] })
222
+ if include_null_comparators? column, ui_options: ui_options
223
+ select_options.concat(ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] })
224
+ end
215
225
  end
216
226
  select_options
217
227
  end
@@ -338,7 +348,7 @@ module ActiveScaffold
338
348
  options_for_select(active_scaffold_search_datetime_trend_units(column), current_search['unit']),
339
349
  class: 'text-input')
340
350
  ]
341
- show = current_search.key?(:show) ? current_search[:show] : current_search['opt'] == 'PAST' || current_search['opt'] == 'FUTURE'
351
+ show = current_search.key?(:show) ? current_search[:show] : %w[PAST FUTURE].include?(current_search['opt'])
342
352
  content_tag('span', safe_join(trend_controls, ' '),
343
353
  id: "#{options[:id]}_trend", class: 'search-date-trend',
344
354
  style: ('display: none' unless show))
@@ -407,7 +417,7 @@ module ActiveScaffold
407
417
  visibles = []
408
418
  hiddens = []
409
419
  columns.each_column do |column|
410
- next unless column.respond_to?(:each_column) || column.search_sql
420
+ next unless column.respond_to?(:each_column) || column.searchable?
411
421
 
412
422
  if search_config.optional_columns.include?(column.name) && !searched_by?(column)
413
423
  hiddens << column
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  # Helpers that assist with the rendering of a List Column
@@ -27,14 +29,14 @@ module ActiveScaffold
27
29
  end
28
30
 
29
31
  def active_scaffold_show_horizontal(record, column, ui_options: column.options)
30
- raise ':horizontal show_ui must be used on association column' unless column.association
32
+ raise ArgumentError, ':horizontal show_ui must be used on association column' unless column.association
31
33
 
32
34
  vars = {column: column, parent_record: record, show_partial: :horizontal}
33
35
  render partial: 'show_association', locals: vars.merge(ui_options.slice(:tabbed_by, :tab_value, :tab_id))
34
36
  end
35
37
 
36
38
  def active_scaffold_show_vertical(record, column, ui_options: column.options)
37
- raise ':vertical show_ui must be used on association column' unless column.association
39
+ raise ArgumentError, ':vertical show_ui must be used on association column' unless column.association
38
40
 
39
41
  render partial: 'show_association', locals: {column: column, parent_record: record, show_partial: :vertical}
40
42
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  # Helpers that assist with rendering of tabs in forms
@@ -59,13 +61,13 @@ module ActiveScaffold
59
61
  end
60
62
 
61
63
  def active_scaffold_tab(label, tab_id, active)
62
- content_tag :li, class: "nav-item #{:active if active}" do
63
- link_to(label, "##{tab_id}", class: 'nav-link', data: {toggle: :tab})
64
+ content_tag :li, class: 'nav-item' do
65
+ link_to(label, "##{tab_id}", class: "nav-link #{:active if active}", data: {bs_toggle: :tab})
64
66
  end
65
67
  end
66
68
 
67
69
  def active_scaffold_tab_content(tab_id, active, content)
68
- content_tag(:div, content, class: "tab-pane fade#{' in active' if active}", id: tab_id)
70
+ content_tag(:div, content, class: "tab-pane fade#{' active show' if active}", id: tab_id)
69
71
  end
70
72
 
71
73
  def active_scaffold_tabs_for(column, record, subsection_id, tab_options, used_tabs)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Helpers
3
5
  # All extra helpers that should be included in the View.
@@ -20,9 +22,7 @@ module ActiveScaffold
20
22
  ##
21
23
 
22
24
  def active_scaffold_controller_for(klass)
23
- ActiveScaffold::Registry.cache :as_controller, klass do
24
- controller.class.active_scaffold_controller_for(klass)
25
- end
25
+ controller.class.active_scaffold_controller_for(klass)
26
26
  end
27
27
 
28
28
  ##
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module MarkedModel
3
5
  # This is a module aimed at making the make session_stored marked_records available to ActiveRecord models
@@ -5,11 +7,12 @@ module ActiveScaffold
5
7
  def self.included(base)
6
8
  base.class_eval do
7
9
  extend ClassMethods
10
+
8
11
  scope :as_marked, -> { where(primary_key => marked_record_ids) }
9
12
  end
10
13
  end
11
14
 
12
- def as_marked
15
+ def as_marked # rubocop:disable Naming/PredicateMethod
13
16
  marked_records.include?(id.to_s)
14
17
  end
15
18
 
@@ -26,7 +29,7 @@ module ActiveScaffold
26
29
  ActiveScaffold::Registry.marked_records ||= {}
27
30
  end
28
31
 
29
- def marked_records=(marked)
32
+ def marked_records=(marked) # rubocop:disable Rails/Delegate
30
33
  ActiveScaffold::Registry.marked_records = marked
31
34
  end
32
35
 
@@ -36,8 +39,6 @@ module ActiveScaffold
36
39
  end
37
40
 
38
41
  # Instance-level access to the marked_records
39
- def marked_records
40
- self.class.marked_records
41
- end
42
+ delegate :marked_records, to: :class
42
43
  end
43
44
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module OrmChecks
3
5
  class << self