active_scaffold 3.5.5 → 3.6.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.
- checksums.yaml +4 -4
- data/{CHANGELOG → CHANGELOG.rdoc} +75 -0
- data/README.md +21 -10
- data/app/assets/javascripts/active_scaffold.js.erb +0 -1
- data/app/assets/javascripts/jquery/active_scaffold.js +98 -7
- data/app/assets/stylesheets/active_scaffold_colors.scss +1 -1
- data/app/assets/stylesheets/active_scaffold_layout.css +52 -29
- data/app/views/active_scaffold_overrides/_base_form.html.erb +2 -2
- data/app/views/active_scaffold_overrides/_form.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_form_association.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +3 -2
- data/app/views/active_scaffold_overrides/_form_association_record.html.erb +26 -10
- data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +4 -4
- data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_list.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_list_header.html.erb +5 -7
- data/app/views/active_scaffold_overrides/_list_messages.html.erb +1 -0
- data/app/views/active_scaffold_overrides/_list_record.html.erb +4 -5
- data/app/views/active_scaffold_overrides/_list_with_header.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_messages.html.erb +1 -0
- data/app/views/active_scaffold_overrides/_refresh_list.js.erb +4 -0
- data/app/views/active_scaffold_overrides/_render_field.js.erb +2 -1
- data/app/views/active_scaffold_overrides/_show_association_horizontal.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_show_columns.html.erb +2 -2
- data/app/views/active_scaffold_overrides/_show_horizontal_record.html.erb +4 -4
- data/app/views/active_scaffold_overrides/_update_calculations.js.erb +1 -1
- data/app/views/active_scaffold_overrides/_update_column.js.erb +2 -2
- data/app/views/active_scaffold_overrides/_vertical_subform.html.erb +2 -2
- data/app/views/active_scaffold_overrides/action_confirmation.html.erb +2 -2
- data/app/views/active_scaffold_overrides/delete.html.erb +2 -2
- data/app/views/active_scaffold_overrides/on_action_update.js.erb +16 -6
- data/app/views/active_scaffold_overrides/on_update.js.erb +1 -1
- data/app/views/active_scaffold_overrides/row.js.erb +1 -1
- data/app/views/active_scaffold_overrides/update_column.js.erb +2 -2
- data/config/locales/de.yml +2 -1
- data/config/locales/en.yml +1 -0
- data/config/locales/es.yml +1 -0
- data/config/locales/fr.yml +2 -1
- data/config/locales/hu.yml +1 -0
- data/config/locales/ja.yml +1 -0
- data/config/locales/ru.yml +1 -0
- data/lib/active_scaffold.rb +19 -16
- data/lib/active_scaffold/actions/common_search.rb +11 -8
- data/lib/active_scaffold/actions/core.rb +91 -70
- data/lib/active_scaffold/actions/create.rb +28 -28
- data/lib/active_scaffold/actions/delete.rb +3 -3
- data/lib/active_scaffold/actions/field_search.rb +53 -43
- data/lib/active_scaffold/actions/list.rb +111 -27
- data/lib/active_scaffold/actions/nested.rb +65 -48
- data/lib/active_scaffold/actions/search.rb +1 -1
- data/lib/active_scaffold/actions/show.rb +4 -4
- data/lib/active_scaffold/actions/subform.rb +23 -22
- data/lib/active_scaffold/actions/update.rb +96 -77
- data/lib/active_scaffold/active_record_permissions.rb +2 -11
- data/lib/active_scaffold/attribute_params.rb +102 -94
- data/lib/active_scaffold/bridges.rb +8 -8
- data/lib/active_scaffold/bridges/active_storage.rb +6 -0
- data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +34 -0
- data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +54 -0
- data/lib/active_scaffold/bridges/active_storage/form_ui.rb +22 -0
- data/lib/active_scaffold/bridges/active_storage/list_ui.rb +36 -0
- data/lib/active_scaffold/bridges/bitfields.rb +2 -1
- data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
- data/lib/active_scaffold/bridges/bitfields/list_ui.rb +19 -0
- data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +3 -12
- data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -2
- data/lib/active_scaffold/bridges/chosen/helpers.rb +7 -6
- data/lib/active_scaffold/bridges/date_picker/ext.rb +0 -13
- data/lib/active_scaffold/bridges/date_picker/helper.rb +49 -44
- data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
- data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +3 -3
- data/lib/active_scaffold/bridges/file_column/form_ui.rb +3 -3
- data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +10 -7
- data/lib/active_scaffold/bridges/paper_trail.rb +1 -1
- data/lib/active_scaffold/bridges/paper_trail/actions.rb +3 -1
- data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
- data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +2 -2
- data/lib/active_scaffold/bridges/record_select/helpers.rb +15 -17
- data/lib/active_scaffold/bridges/shared/date_bridge.rb +20 -19
- data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
- data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +21 -4
- data/lib/active_scaffold/config/base.rb +133 -41
- data/lib/active_scaffold/config/core.rb +146 -18
- data/lib/active_scaffold/config/delete.rb +14 -1
- data/lib/active_scaffold/config/field_search.rb +7 -1
- data/lib/active_scaffold/config/form.rb +10 -1
- data/lib/active_scaffold/config/list.rb +39 -13
- data/lib/active_scaffold/config/mark.rb +4 -2
- data/lib/active_scaffold/config/nested.rb +16 -17
- data/lib/active_scaffold/config/search.rb +9 -0
- data/lib/active_scaffold/config/show.rb +4 -0
- data/lib/active_scaffold/config/update.rb +4 -0
- data/lib/active_scaffold/configurable.rb +14 -7
- data/lib/active_scaffold/constraints.rb +22 -20
- data/lib/active_scaffold/core.rb +67 -28
- data/lib/active_scaffold/data_structures/action_columns.rb +50 -59
- data/lib/active_scaffold/data_structures/action_link.rb +50 -20
- data/lib/active_scaffold/data_structures/action_links.rb +15 -13
- data/lib/active_scaffold/data_structures/association/abstract.rb +38 -15
- data/lib/active_scaffold/data_structures/association/active_mongoid.rb +2 -6
- data/lib/active_scaffold/data_structures/association/active_record.rb +6 -2
- data/lib/active_scaffold/data_structures/association/mongoid.rb +0 -3
- data/lib/active_scaffold/data_structures/column.rb +75 -66
- data/lib/active_scaffold/data_structures/columns.rb +3 -2
- data/lib/active_scaffold/data_structures/nested_info.rb +33 -19
- data/lib/active_scaffold/data_structures/set.rb +8 -0
- data/lib/active_scaffold/data_structures/sorting.rb +10 -2
- data/lib/active_scaffold/delayed_setup.rb +16 -5
- data/lib/active_scaffold/extensions/action_controller_rendering.rb +3 -2
- data/lib/active_scaffold/extensions/action_view_rendering.rb +93 -32
- data/lib/active_scaffold/extensions/cow_proxy.rb +95 -0
- data/lib/active_scaffold/extensions/ice_nine.rb +36 -0
- data/lib/active_scaffold/extensions/left_outer_joins.rb +8 -33
- data/lib/active_scaffold/extensions/localize.rb +3 -1
- data/lib/active_scaffold/extensions/routing_mapper.rb +6 -45
- data/lib/active_scaffold/extensions/to_label.rb +3 -2
- data/lib/active_scaffold/extensions/unsaved_record.rb +2 -4
- data/lib/active_scaffold/finder.rb +110 -77
- data/lib/active_scaffold/helpers/action_link_helpers.rb +62 -36
- data/lib/active_scaffold/helpers/association_helpers.rb +18 -16
- data/lib/active_scaffold/helpers/controller_helpers.rb +34 -10
- data/lib/active_scaffold/helpers/form_column_helpers.rb +196 -124
- data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -1
- data/lib/active_scaffold/helpers/id_helpers.rb +6 -2
- data/lib/active_scaffold/helpers/list_column_helpers.rb +90 -57
- data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
- data/lib/active_scaffold/helpers/search_column_helpers.rb +43 -41
- data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
- data/lib/active_scaffold/helpers/view_helpers.rb +39 -36
- data/lib/active_scaffold/marked_model.rb +2 -2
- data/lib/active_scaffold/orm_checks.rb +3 -7
- data/lib/active_scaffold/paginator.rb +7 -7
- data/lib/active_scaffold/registry.rb +33 -0
- data/lib/active_scaffold/responds_to_parent.rb +8 -11
- data/lib/active_scaffold/tableless.rb +83 -67
- data/lib/active_scaffold/version.rb +2 -2
- data/lib/generators/active_scaffold/controller_generator.rb +2 -2
- data/lib/generators/active_scaffold/install_generator.rb +52 -4
- data/lib/generators/active_scaffold/resource_generator.rb +2 -2
- data/shoulda_macros/macros.rb +3 -1
- data/test/bridges/date_picker_test.rb +1 -2
- data/test/bridges/paperclip_test.rb +6 -6
- data/test/class_with_finder.rb +2 -2
- data/test/company.rb +4 -4
- data/test/config/create_test.rb +4 -2
- data/test/config/nested_test.rb +1 -1
- data/test/config/show_test.rb +1 -1
- data/test/config/update_test.rb +7 -6
- data/test/data_structures/action_columns_test.rb +2 -2
- data/test/data_structures/action_links_test.rb +1 -1
- data/test/data_structures/column_test.rb +3 -6
- data/test/data_structures/columns_test.rb +2 -2
- data/test/data_structures/sorting_test.rb +7 -0
- data/test/extensions/action_view_rendering_test.rb +20 -0
- data/test/extensions/active_record_test.rb +4 -4
- data/test/extensions/routing_mapper_test.rb +2 -2
- data/test/helpers/list_column_helpers_test.rb +3 -1
- data/test/misc/active_record_permissions_test.rb +3 -11
- data/test/misc/attribute_params_test.rb +12 -8
- data/test/misc/calculation_test.rb +1 -1
- data/test/misc/configurable_test.rb +10 -10
- data/test/misc/constraints_test.rb +3 -3
- data/test/misc/convert_numbers_format_test.rb +7 -3
- data/test/misc/lang_test.rb +1 -1
- data/test/misc/parse_datetime_test.rb +3 -4
- data/test/misc/tableless_test.rb +14 -0
- data/test/mock_app/Rakefile +1 -1
- data/test/mock_app/app/assets/config/manifest.js +0 -0
- data/test/mock_app/app/controllers/cars_controller.rb +1 -0
- data/test/mock_app/app/controllers/people_controller.rb +5 -1
- data/test/mock_app/app/controllers/roles_controller.rb +4 -0
- data/test/mock_app/app/views/active_scaffold_overrides/_form.html.erb +2 -0
- data/test/mock_app/app/views/active_scaffold_overrides/list.html.erb +2 -0
- data/test/mock_app/app/views/people/_first_name_form_column.html.erb +2 -0
- data/test/mock_app/app/views/people/_form.html.erb +2 -0
- data/test/mock_app/app/views/people/list.html.erb +2 -0
- data/test/mock_app/config/application.rb +2 -1
- data/test/mock_app/config/boot.rb +1 -1
- data/test/mock_app/config/environment.rb +2 -2
- data/test/mock_app/config/routes.rb +4 -1
- data/test/mock_app/db/schema.rb +2 -0
- data/test/performance/list_cars_performance_test.rb +34 -0
- data/test/performance/list_people_performance_test.rb +31 -0
- data/test/performance_test_help.rb +3 -0
- data/test/test_helper.rb +12 -4
- metadata +71 -15
- data/app/assets/javascripts/prototype/rico_corner.js +0 -370
- data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +0 -7
@@ -24,9 +24,9 @@ module ActiveScaffold
|
|
24
24
|
ul_options = record_or_ul_options if ul_options.nil? && record_or_ul_options.is_a?(Hash)
|
25
25
|
record = record_or_ul_options unless record_or_ul_options.is_a?(Hash)
|
26
26
|
html = content_tag :ul, ul_options do
|
27
|
-
safe_join
|
27
|
+
safe_join(links.map { |link| content_tag :li, link })
|
28
28
|
end
|
29
|
-
raw "ActiveScaffold.display_dynamic_action_group('#{get_action_link_id action_link, record}', '#{escape_javascript html}');"
|
29
|
+
raw "ActiveScaffold.display_dynamic_action_group('#{get_action_link_id action_link, record}', '#{escape_javascript html}');" # rubocop:disable Rails/OutputSafety
|
30
30
|
end
|
31
31
|
|
32
32
|
def display_action_links(action_links, record, options, &block)
|
@@ -67,7 +67,7 @@ module ActiveScaffold
|
|
67
67
|
html_classes << 'top' if options[:first_action]
|
68
68
|
group_tag = :li
|
69
69
|
end
|
70
|
-
content = content_tag(group_tag, :class =>
|
70
|
+
content = content_tag(group_tag, :class => html_classes.presence, :onclick => ('' if hover_via_click?)) do
|
71
71
|
content_tag(:div, as_(link.label), :class => link.name.to_s.downcase) << content_tag(:ul, content)
|
72
72
|
end
|
73
73
|
else
|
@@ -79,13 +79,15 @@ module ActiveScaffold
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def render_action_link(link, record = nil, options = {})
|
82
|
-
if link.action.nil? || link.column
|
83
|
-
link = action_link_to_inline_form(link, record) if link.column
|
82
|
+
if link.action.nil? || link.column&.association&.polymorphic?
|
83
|
+
link = action_link_to_inline_form(link, record) if link.column&.association
|
84
84
|
options[:authorized] = false if link.action.nil? || link.controller.nil?
|
85
85
|
options.delete :link if link.crud_type == :create
|
86
86
|
end
|
87
87
|
if link.action.nil? || (link.type == :member && options.key?(:authorized) && !options[:authorized])
|
88
|
-
|
88
|
+
html_class = "disabled #{link.action}#{" #{link.html_options[:class]}" if link.html_options[:class].present?}"
|
89
|
+
html_options = {:link => action_link_text(link, options), :class => html_class, :title => options[:not_authorized_reason]}
|
90
|
+
action_link_html(link, nil, html_options, record)
|
89
91
|
else
|
90
92
|
url = action_link_url(link, record)
|
91
93
|
html_options = action_link_html_options(link, record, options)
|
@@ -95,9 +97,9 @@ module ActiveScaffold
|
|
95
97
|
|
96
98
|
# setup the action link to inline form
|
97
99
|
def action_link_to_inline_form(link, record)
|
98
|
-
link = link.
|
100
|
+
link = link.dup
|
99
101
|
associated = record.send(link.column.association.name)
|
100
|
-
if link.column.association
|
102
|
+
if link.column.association&.polymorphic?
|
101
103
|
link.controller = controller_path_for_activerecord(associated.class)
|
102
104
|
return link if link.controller.nil?
|
103
105
|
end
|
@@ -106,12 +108,12 @@ module ActiveScaffold
|
|
106
108
|
end
|
107
109
|
|
108
110
|
def configure_column_link(link, record, associated, actions = nil)
|
109
|
-
actions ||= link.
|
111
|
+
actions ||= link.controller_actions || []
|
110
112
|
if column_empty?(associated) # if association is empty, we only can link to create form
|
111
113
|
if actions.include?(:new)
|
112
114
|
link.action = 'new'
|
113
115
|
link.crud_type = :create
|
114
|
-
link.label ||=
|
116
|
+
link.label ||= :create_new
|
115
117
|
end
|
116
118
|
elsif actions.include?(:edit)
|
117
119
|
link.action = 'edit'
|
@@ -137,7 +139,7 @@ module ActiveScaffold
|
|
137
139
|
def column_link_authorized?(link, column, record, associated)
|
138
140
|
if column.association
|
139
141
|
associated_for_authorized =
|
140
|
-
if column.association.collection? ||
|
142
|
+
if column.association.collection? || associated.nil?
|
141
143
|
column.association.klass
|
142
144
|
else
|
143
145
|
associated
|
@@ -160,7 +162,7 @@ module ActiveScaffold
|
|
160
162
|
end
|
161
163
|
|
162
164
|
def cache_action_link_url?(link, record)
|
163
|
-
active_scaffold_config.cache_action_link_urls && link.type == :member && !link.dynamic_parameters.is_a?(Proc) && !sti_record?(record)
|
165
|
+
active_scaffold_config.user.cache_action_link_urls && link.type == :member && !link.dynamic_parameters.is_a?(Proc) && !sti_record?(record)
|
164
166
|
end
|
165
167
|
|
166
168
|
def cached_action_link_url(link, record)
|
@@ -178,8 +180,8 @@ module ActiveScaffold
|
|
178
180
|
|
179
181
|
def replace_id_params_in_action_link_url(link, record, url)
|
180
182
|
url = record ? url.sub('--ID--', record.to_param.to_s) : url.clone
|
181
|
-
if link.column
|
182
|
-
child_id = record.send(link.column.association.name)
|
183
|
+
if link.column&.association&.singular?
|
184
|
+
child_id = record.send(link.column.association.name)&.to_param
|
183
185
|
if child_id.present?
|
184
186
|
url.sub!('--CHILD_ID--', child_id)
|
185
187
|
else
|
@@ -209,6 +211,18 @@ module ActiveScaffold
|
|
209
211
|
url
|
210
212
|
end
|
211
213
|
|
214
|
+
def column_in_params_conditions?(key)
|
215
|
+
if key =~ /!$/
|
216
|
+
conditions_from_params[1..-1].any? { |node| node.left.name.to_s == key[0..-2] }
|
217
|
+
else
|
218
|
+
conditions_from_params[0].include?(key)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def ignore_param_for_nested?(key)
|
223
|
+
NESTED_PARAMS.include?(key) || column_in_params_conditions?(key) || (nested? && nested.param_name == key)
|
224
|
+
end
|
225
|
+
|
212
226
|
def query_string_for_action_links(link)
|
213
227
|
if defined?(@query_string) && link.parameters.none? { |k, _| @query_string_params.include? k }
|
214
228
|
return [@query_string, @non_nested_query_string]
|
@@ -224,12 +238,17 @@ module ActiveScaffold
|
|
224
238
|
keep = false
|
225
239
|
next
|
226
240
|
end
|
227
|
-
if
|
241
|
+
if ignore_param_for_nested?(key)
|
228
242
|
non_nested_query_string_options[key] = value
|
229
243
|
else
|
230
244
|
query_string_options[key] = value
|
231
245
|
end
|
232
246
|
end
|
247
|
+
if nested_singular_association? && action_name == 'index'
|
248
|
+
# pass current path as return_to, for nested listing on singular association, so forms doesn't return to parent listing
|
249
|
+
@query_string_params << :return_to
|
250
|
+
non_nested_query_string_options[:return_to] = request.fullpath
|
251
|
+
end
|
233
252
|
|
234
253
|
query_string = query_string_options.to_query if query_string_options.present?
|
235
254
|
if non_nested_query_string_options.present?
|
@@ -243,7 +262,7 @@ module ActiveScaffold
|
|
243
262
|
end
|
244
263
|
|
245
264
|
def cache_action_link_url_options?(link, record)
|
246
|
-
active_scaffold_config.cache_action_link_urls && (link.type == :collection || !link.dynamic_parameters.is_a?(Proc)) && !sti_record?(record)
|
265
|
+
active_scaffold_config.user.cache_action_link_urls && (link.type == :collection || !link.dynamic_parameters.is_a?(Proc)) && !sti_record?(record)
|
247
266
|
end
|
248
267
|
|
249
268
|
def cached_action_link_url_options(link, record)
|
@@ -290,8 +309,8 @@ module ActiveScaffold
|
|
290
309
|
missing_options, url_options = url.partition { |_, v| v.nil? }
|
291
310
|
replacements = {}
|
292
311
|
replacements['--ID--'] = record.id.to_s if record
|
293
|
-
if link.column
|
294
|
-
replacements['--CHILD_ID--'] = record.send(link.column.association.name)
|
312
|
+
if link.column&.association&.singular?
|
313
|
+
replacements['--CHILD_ID--'] = record.send(link.column.association.name)&.id.to_s
|
295
314
|
elsif nested?
|
296
315
|
replacements['--CHILD_ID--'] = params[nested.param_name].to_s
|
297
316
|
end
|
@@ -303,7 +322,7 @@ module ActiveScaffold
|
|
303
322
|
|
304
323
|
def action_link_selected?(link, record)
|
305
324
|
missing_options, url_options = replaced_action_link_url_options(link, record)
|
306
|
-
safe_params =
|
325
|
+
safe_params = params.to_unsafe_h
|
307
326
|
(url_options - safe_params.to_a).blank? && missing_options.all? { |k, _| params[k].nil? }
|
308
327
|
end
|
309
328
|
|
@@ -316,7 +335,7 @@ module ActiveScaffold
|
|
316
335
|
html_options[:method] = link.method if link.method != :get
|
317
336
|
|
318
337
|
html_options[:data] ||= {}
|
319
|
-
html_options[:data][:confirm] = link.confirm(h(record
|
338
|
+
html_options[:data][:confirm] = link.confirm(h(record&.to_label)) if link.confirm?
|
320
339
|
if !options[:page] && !options[:popup] && (options[:inline] || link.inline?)
|
321
340
|
html_options[:class] << ' as_action'
|
322
341
|
html_options[:data][:position] = link.position if link.position
|
@@ -331,7 +350,10 @@ module ActiveScaffold
|
|
331
350
|
html_options[:class] << ' active' if action_link_selected?(link, record)
|
332
351
|
end
|
333
352
|
|
334
|
-
|
353
|
+
if !options[:page] && !options[:inline] && (options[:popup] || link.popup?)
|
354
|
+
html_options[:target] = '_blank'
|
355
|
+
html_options[:rel] = [html_options[:rel], 'noopener noreferrer'].compact.join(' ')
|
356
|
+
end
|
335
357
|
html_options[:id] = link_id
|
336
358
|
if link.dhtml_confirm?
|
337
359
|
unless link.inline?
|
@@ -344,20 +366,23 @@ module ActiveScaffold
|
|
344
366
|
html_options
|
345
367
|
end
|
346
368
|
|
347
|
-
def get_action_link_id(link, record = nil
|
348
|
-
column
|
349
|
-
if column
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
369
|
+
def get_action_link_id(link, record = nil)
|
370
|
+
column = link.column
|
371
|
+
if column&.association && record
|
372
|
+
associated = record.send(column.association.name) unless column.association.collection?
|
373
|
+
id =
|
374
|
+
if associated
|
375
|
+
"#{column.association.name}-#{associated.id}-#{record.id}"
|
376
|
+
else
|
377
|
+
"#{column.association.name}-#{record.id}"
|
378
|
+
end
|
379
|
+
end
|
380
|
+
id ||= record&.id&.to_s || (nested? ? nested_parent_id.to_s : '')
|
381
|
+
action_link_id = ActiveScaffold::Registry.cache :action_link_id, link.name_to_cache do
|
382
|
+
action_id = "#{id_from_controller("#{link.controller}-") if params[:parent_controller] || (link.controller && link.controller != controller.controller_path)}#{link.action}"
|
383
|
+
action_link_id(action_id, '--ID--')
|
357
384
|
end
|
358
|
-
|
359
|
-
action_id = "#{id_from_controller("#{link.controller}-") if params[:parent_controller] || (link.controller && link.controller != controller.controller_path)}#{link.action}"
|
360
|
-
action_link_id(action_id, id)
|
385
|
+
action_link_id.sub('--ID--', id)
|
361
386
|
end
|
362
387
|
|
363
388
|
def action_link_html(link, url, html_options, record)
|
@@ -371,15 +396,16 @@ module ActiveScaffold
|
|
371
396
|
end
|
372
397
|
|
373
398
|
def url_options_for_nested_link(column, record, link, url_options)
|
374
|
-
if column
|
399
|
+
if column&.association
|
375
400
|
url_options[:parent_scaffold] = controller_path
|
376
401
|
url_options[column.model.name.foreign_key.to_sym] = url_options.delete(:id)
|
377
402
|
url_options[:id] = if column.association.singular? && url_options[:action].to_sym != :index
|
378
403
|
'--CHILD_ID--'
|
379
404
|
end
|
380
|
-
elsif link.parameters
|
405
|
+
elsif link.parameters&.dig(:named_scope)
|
381
406
|
url_options[:parent_scaffold] = controller_path
|
382
407
|
url_options[active_scaffold_config.model.name.foreign_key.to_sym] = url_options.delete(:id)
|
408
|
+
url_options[:id] = nil
|
383
409
|
end
|
384
410
|
end
|
385
411
|
|
@@ -16,11 +16,8 @@ module ActiveScaffold
|
|
16
16
|
def association_options_find(association, conditions = nil, klass = nil, record = nil)
|
17
17
|
if klass.nil? && association.polymorphic?
|
18
18
|
class_name = record.send(association.foreign_type) if association.belongs_to?
|
19
|
-
if class_name.
|
20
|
-
|
21
|
-
else
|
22
|
-
return []
|
23
|
-
end
|
19
|
+
return [] if class_name.blank?
|
20
|
+
klass = class_name.constantize
|
24
21
|
cache = !block_given?
|
25
22
|
else
|
26
23
|
cache = !block_given? && klass.nil?
|
@@ -32,12 +29,14 @@ module ActiveScaffold
|
|
32
29
|
klass = association_klass_scoped(association, klass, record)
|
33
30
|
relation = klass.where(conditions)
|
34
31
|
column = column_for_association(association, record)
|
35
|
-
if column
|
32
|
+
if column&.includes
|
36
33
|
include_assoc = column.includes.find { |assoc| assoc.is_a?(Hash) && assoc.include?(association.name) }
|
37
34
|
relation = relation.includes(include_assoc[association.name]) if include_assoc
|
38
35
|
end
|
39
|
-
if column
|
40
|
-
|
36
|
+
if column&.sort && column.sort&.dig(:sql)
|
37
|
+
# with threasafe enabled, column.sort[:sql] returns proxied strings and
|
38
|
+
# regexp capture won't work, which rails uses internally, so to_s is needed
|
39
|
+
relation = relation.order(Array(column.sort[:sql]).map(&:to_s))
|
41
40
|
end
|
42
41
|
relation = yield(relation) if block_given?
|
43
42
|
relation.to_a
|
@@ -45,16 +44,20 @@ module ActiveScaffold
|
|
45
44
|
end
|
46
45
|
|
47
46
|
def column_for_association(association, record)
|
48
|
-
active_scaffold_config_for(record.class).columns[association.name]
|
47
|
+
active_scaffold_config_for(record.class).columns[association.name]
|
48
|
+
rescue StandardError => e
|
49
|
+
message = "Error on config for #{record.class.name}:"
|
50
|
+
Rails.logger.warn "#{message}\n#{e.message}\n#{e.backtrace.join("\n")}"
|
51
|
+
nil
|
49
52
|
end
|
50
53
|
|
51
54
|
def association_klass_scoped(association, klass, record)
|
52
|
-
if nested? && nested.through_association? && nested.child_association
|
55
|
+
if nested? && nested.through_association? && nested.child_association&.through_reflection == association
|
53
56
|
# only ActiveRecord associations
|
54
57
|
if nested.association.through_reflection.collection?
|
55
58
|
nested_parent_record.send(nested.association.through_reflection.name)
|
56
59
|
else
|
57
|
-
klass.where(association.association_primary_key => nested_parent_record.send(nested.association.through_reflection.name)
|
60
|
+
klass.where(association.association_primary_key => nested_parent_record.send(nested.association.through_reflection.name)&.id)
|
58
61
|
end
|
59
62
|
else
|
60
63
|
klass
|
@@ -65,7 +68,7 @@ module ActiveScaffold
|
|
65
68
|
def sorted_association_options_find(association, conditions = nil, record = nil)
|
66
69
|
options = association_options_find(association, conditions, nil, record)
|
67
70
|
column = column_for_association(association, record)
|
68
|
-
unless column
|
71
|
+
unless column&.sort && column.sort&.dig(:sql)
|
69
72
|
method = column.options[:label_method] if column
|
70
73
|
options = options.sort_by(&(method || :to_label).to_sym)
|
71
74
|
end
|
@@ -86,10 +89,9 @@ module ActiveScaffold
|
|
86
89
|
# Check association.name to specialize the conditions per-column.
|
87
90
|
def options_for_association_conditions(association, record = nil)
|
88
91
|
return nil if association.through?
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
92
|
+
return nil unless association.has_one? || association.has_many?
|
93
|
+
# Find only orphaned objects
|
94
|
+
{association.foreign_key => nil}
|
93
95
|
end
|
94
96
|
|
95
97
|
def record_select_params_for_add_existing(association, edit_associated_url_options, record)
|
@@ -2,25 +2,38 @@ module ActiveScaffold
|
|
2
2
|
module Helpers
|
3
3
|
module ControllerHelpers
|
4
4
|
def self.included(controller)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
return unless controller.respond_to? :helper_method
|
6
|
+
controller.class_eval do
|
7
|
+
helper_method :params_for, :conditions_from_params, :render_parent?,
|
8
|
+
:main_path_to_return, :render_parent_options,
|
9
|
+
:render_parent_action, :nested_singular_association?,
|
10
|
+
:main_form_controller, :build_associated,
|
11
|
+
:generate_temporary_id, :generated_id,
|
12
|
+
:active_scaffold_config_for
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
include ActiveScaffold::Helpers::IdHelpers
|
17
17
|
|
18
|
+
def active_scaffold_config_for(klass)
|
19
|
+
config = self.class.active_scaffold_config_for(klass)
|
20
|
+
if ActiveScaffold.threadsafe
|
21
|
+
config.user || config.new_user_settings({}, {})
|
22
|
+
else
|
23
|
+
config
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
18
27
|
def generate_temporary_id(record = nil, generated_id = nil)
|
19
28
|
(generated_id || (Time.now.to_f * 1000).to_i.to_s).tap do |id|
|
20
|
-
|
29
|
+
cache_generated_id record, id
|
21
30
|
end
|
22
31
|
end
|
23
32
|
|
33
|
+
def cache_generated_id(record, generated_id)
|
34
|
+
(@temporary_ids ||= {})[record.class.name] = generated_id if record && generated_id
|
35
|
+
end
|
36
|
+
|
24
37
|
def generated_id(record)
|
25
38
|
@temporary_ids[record.class.name] if record && @temporary_ids
|
26
39
|
end
|
@@ -91,7 +104,7 @@ module ActiveScaffold
|
|
91
104
|
else
|
92
105
|
exclude_parameters = %i[utf8 associated_id]
|
93
106
|
parameters = {}
|
94
|
-
if params[:parent_scaffold] &&
|
107
|
+
if params[:parent_scaffold] && nested_singular_association?
|
95
108
|
parameters[:controller] = params[:parent_scaffold]
|
96
109
|
exclude_parameters.concat [nested.param_name, :association, :parent_scaffold]
|
97
110
|
# parameters[:eid] = params[:parent_scaffold] # not neeeded anymore?
|
@@ -167,6 +180,17 @@ module ActiveScaffold
|
|
167
180
|
end
|
168
181
|
end
|
169
182
|
end
|
183
|
+
|
184
|
+
def save_record_to_association(record, association, value, reverse = nil)
|
185
|
+
return unless association
|
186
|
+
if association.collection?
|
187
|
+
record.association(association.name).target << value
|
188
|
+
elsif reverse&.belongs_to?
|
189
|
+
value.send("#{reverse.name}=", record)
|
190
|
+
else
|
191
|
+
record.send("#{association.name}=", value)
|
192
|
+
end
|
193
|
+
end
|
170
194
|
end
|
171
195
|
end
|
172
196
|
end
|
@@ -16,47 +16,43 @@ module ActiveScaffold
|
|
16
16
|
# first, check if the dev has created an override for this specific field
|
17
17
|
if (method = override_form_field(column))
|
18
18
|
send(method, record, options)
|
19
|
+
|
19
20
|
# second, check if the dev has specified a valid form_ui for this column
|
20
21
|
elsif column.form_ui && (method = override_input(column.form_ui))
|
21
22
|
send(method, column, options)
|
22
|
-
|
23
|
-
|
24
|
-
if column.
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
if column.column.type == :string && options[:maxlength].blank?
|
46
|
-
options[:maxlength] = column.column.limit
|
47
|
-
options[:size] ||= options[:maxlength].to_i > 30 ? 30 : options[:maxlength]
|
48
|
-
end
|
49
|
-
options[:value] = format_number_value(record.send(column.name), column.options) if column.number?
|
50
|
-
text_field(:record, column.name, options.merge(column.options).except(:format))
|
51
|
-
end
|
23
|
+
|
24
|
+
elsif column.association
|
25
|
+
# 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
|
27
|
+
|
28
|
+
# its an association and nothing is specified, we will assume form_ui :select
|
29
|
+
active_scaffold_input_select(column, options)
|
30
|
+
|
31
|
+
elsif column.virtual?
|
32
|
+
options[:value] = format_number_value(record.send(column.name), column.options) if column.number?
|
33
|
+
active_scaffold_input_virtual(column, options)
|
34
|
+
|
35
|
+
elsif (method = override_input(column.column.type)) # regular model attribute column
|
36
|
+
# if we (or someone else) have created a custom render option for the column type, use that
|
37
|
+
send(method, column, options)
|
38
|
+
|
39
|
+
else # final ultimate fallback: use rails' generic input method
|
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
|
45
|
+
options[:size] ||= options[:maxlength].to_i > 30 ? 30 : options[:maxlength]
|
52
46
|
end
|
47
|
+
options[:value] = format_number_value(record.send(column.name), column.options) if column.number?
|
48
|
+
text_field(:record, column.name, options.merge(column.options).except(:format))
|
53
49
|
end
|
54
50
|
rescue StandardError => e
|
55
51
|
logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
|
56
52
|
raise e
|
57
53
|
end
|
58
54
|
|
59
|
-
def active_scaffold_render_subform_column(column, scope, crud_type, readonly, add_class = false, record = nil)
|
55
|
+
def active_scaffold_render_subform_column(column, scope, crud_type, readonly, add_class = false, record = nil) # rubocop:disable Metrics/ParameterLists
|
60
56
|
if add_class
|
61
57
|
col_class = []
|
62
58
|
col_class << 'required' if column.required?
|
@@ -66,9 +62,7 @@ module ActiveScaffold
|
|
66
62
|
col_class = col_class.join(' ')
|
67
63
|
end
|
68
64
|
if readonly && !record.new_record? || !record.authorized_for?(:crud_type => crud_type, :column => column.name)
|
69
|
-
|
70
|
-
options[:class] = "#{options[:class]} #{col_class}" if col_class
|
71
|
-
content_tag :span, get_column_value(record, column), options
|
65
|
+
form_attribute(column, record, scope, true, col_class)
|
72
66
|
else
|
73
67
|
renders_as = column_renders_as(column)
|
74
68
|
html = render_column(column, record, renders_as, scope, false, col_class)
|
@@ -77,16 +71,16 @@ module ActiveScaffold
|
|
77
71
|
end
|
78
72
|
end
|
79
73
|
|
80
|
-
def active_scaffold_subform_attributes(column, column_css_class = nil)
|
74
|
+
def active_scaffold_subform_attributes(column, column_css_class = nil, klass = nil)
|
81
75
|
{
|
82
|
-
:class => "sub-form #{active_scaffold_config_for(column.association.klass).subform.layout}-sub-form #{column_css_class} #{column.name}-sub-form",
|
76
|
+
:class => "sub-form #{active_scaffold_config_for(klass || column.association.klass).subform.layout}-sub-form #{column_css_class} #{column.name}-sub-form",
|
83
77
|
:id => sub_form_id(:association => column.name)
|
84
78
|
}
|
85
79
|
end
|
86
80
|
|
87
81
|
# the standard active scaffold options used for textual inputs
|
88
82
|
def active_scaffold_input_text_options(options = {})
|
89
|
-
options[:autocomplete]
|
83
|
+
options[:autocomplete] ||= 'off'
|
90
84
|
options[:class] = "#{options[:class]} text-input".strip
|
91
85
|
options
|
92
86
|
end
|
@@ -97,7 +91,7 @@ module ActiveScaffold
|
|
97
91
|
record = options[:object]
|
98
92
|
|
99
93
|
# Add some HTML5 attributes for in-browser validation and better user experience
|
100
|
-
if column.required? && (!@disable_required_for_new || scope.nil? || record
|
94
|
+
if column.required? && (!@disable_required_for_new || scope.nil? || record&.persisted?)
|
101
95
|
options[:required] = true
|
102
96
|
end
|
103
97
|
options[:placeholder] = column.placeholder if column.placeholder.present?
|
@@ -114,18 +108,20 @@ module ActiveScaffold
|
|
114
108
|
|
115
109
|
def current_form_columns(record, scope, subform_controller = nil)
|
116
110
|
if scope
|
117
|
-
subform_controller.active_scaffold_config.subform.columns.
|
111
|
+
subform_controller.active_scaffold_config.subform.columns.visible_columns_names
|
118
112
|
elsif %i[new create edit update render_field].include? action_name.to_sym
|
119
113
|
# disable update_columns for inplace_edit (GET render_field)
|
120
114
|
return if action_name == 'render_field' && request.get?
|
121
|
-
active_scaffold_config.send(record.new_record? ? :create : :update).columns.
|
115
|
+
active_scaffold_config.send(record.new_record? ? :create : :update).columns.visible_columns_names
|
122
116
|
end
|
123
117
|
end
|
124
118
|
|
125
119
|
def update_columns_options(column, scope, options, force = false)
|
126
120
|
record = options[:object]
|
127
121
|
subform_controller = controller.class.active_scaffold_controller_for(record.class) if scope
|
128
|
-
|
122
|
+
if @main_columns && (scope.nil? || subform_controller == controller.class)
|
123
|
+
form_columns = @main_columns.visible_columns_names
|
124
|
+
end
|
129
125
|
form_columns ||= current_form_columns(record, scope, subform_controller)
|
130
126
|
if force || (form_columns && column.update_columns && (column.update_columns & form_columns).present?)
|
131
127
|
url_params = params_for(:action => 'render_field', :column => column.name, :id => record.to_param)
|
@@ -134,7 +130,7 @@ module ActiveScaffold
|
|
134
130
|
url_params = url_params.except(:parent_scaffold, :association, nested.param_name)
|
135
131
|
end
|
136
132
|
if scope
|
137
|
-
url_params[:parent_controller] ||= url_params[:controller].gsub(
|
133
|
+
url_params[:parent_controller] ||= url_params[:controller].gsub(%r{^/}, '')
|
138
134
|
url_params[:controller] = subform_controller.controller_path
|
139
135
|
url_params[:scope] = scope
|
140
136
|
url_params[:parent_id] = params[:parent_id] || params[:id]
|
@@ -152,8 +148,8 @@ module ActiveScaffold
|
|
152
148
|
{}
|
153
149
|
end
|
154
150
|
|
155
|
-
def render_column(column, record, renders_as, scope = nil, only_value = false, col_class = nil)
|
156
|
-
if partial = override_form_field_partial(column)
|
151
|
+
def render_column(column, record, renders_as, scope = nil, only_value = false, col_class = nil) # rubocop:disable Metrics/ParameterLists
|
152
|
+
if (partial = override_form_field_partial(column))
|
157
153
|
render :partial => partial, :locals => {:column => column, :only_value => only_value, :scope => scope, :col_class => col_class, :record => record}
|
158
154
|
elsif renders_as == :field || override_form_field?(column)
|
159
155
|
form_attribute(column, record, scope, only_value, col_class)
|
@@ -179,17 +175,23 @@ module ActiveScaffold
|
|
179
175
|
else
|
180
176
|
field = active_scaffold_input_for column, scope, column_options
|
181
177
|
end
|
178
|
+
if field
|
179
|
+
field << loading_indicator_tag(:action => :render_field, :id => params[:id]) if column.update_columns
|
180
|
+
field << content_tag(:span, column.description, :class => 'description') if column.description.present?
|
181
|
+
end
|
182
182
|
|
183
183
|
content_tag :dl, attributes do
|
184
|
-
|
185
|
-
|
186
|
-
#{content_tag :span, column.description, :class => 'description' if column.description.present?}
|
187
|
-
</dd>).html_safe
|
184
|
+
content_tag(:dt, label_tag(label_for(column, column_options), form_column_label(column))) <<
|
185
|
+
content_tag(:dd, field)
|
188
186
|
end
|
189
187
|
end
|
190
188
|
|
191
189
|
def label_for(column, options)
|
192
|
-
options[:id] unless column.form_ui == :select && column.association
|
190
|
+
options[:id] unless column.form_ui == :select && column.association&.collection?
|
191
|
+
end
|
192
|
+
|
193
|
+
def form_column_label(column)
|
194
|
+
column.label
|
193
195
|
end
|
194
196
|
|
195
197
|
def subform_label(column, hidden)
|
@@ -205,7 +207,7 @@ module ActiveScaffold
|
|
205
207
|
|
206
208
|
def form_hidden_field(column, record, scope)
|
207
209
|
options = active_scaffold_input_options(column, scope)
|
208
|
-
if column.association
|
210
|
+
if column.association&.collection?
|
209
211
|
associated = record.send(column.name)
|
210
212
|
if associated.blank?
|
211
213
|
hidden_field_tag options[:name], '', options
|
@@ -217,7 +219,7 @@ module ActiveScaffold
|
|
217
219
|
safe_join fields, ''
|
218
220
|
end
|
219
221
|
elsif column.association
|
220
|
-
hidden_field_tag options[:name], record.send(column.name)
|
222
|
+
hidden_field_tag options[:name], record.send(column.name)&.id, options
|
221
223
|
else
|
222
224
|
hidden_field :record, column.name, options.merge(object: record)
|
223
225
|
end
|
@@ -245,7 +247,7 @@ module ActiveScaffold
|
|
245
247
|
end
|
246
248
|
|
247
249
|
def column_show_add_existing(column, record = nil)
|
248
|
-
column.allow_add_existing && options_for_association_count(column.association, record)
|
250
|
+
column.allow_add_existing && options_for_association_count(column.association, record).positive?
|
249
251
|
end
|
250
252
|
|
251
253
|
def column_show_add_new(column, associated, record)
|
@@ -263,7 +265,7 @@ module ActiveScaffold
|
|
263
265
|
def active_scaffold_grouped_options(column, select_options, optgroup)
|
264
266
|
group_column = active_scaffold_config_for(column.association.klass).columns[optgroup]
|
265
267
|
group_label = group_column.options[:label_method] if group_column
|
266
|
-
group_label ||= group_column
|
268
|
+
group_label ||= group_column&.association ? :to_label : :to_s
|
267
269
|
select_options.group_by(&optgroup.to_sym).collect do |group, options|
|
268
270
|
[group.send(group_label), options.collect { |r| [r.send(column.options[:label_method] || :to_label), r.id] }]
|
269
271
|
end
|
@@ -276,9 +278,8 @@ module ActiveScaffold
|
|
276
278
|
end
|
277
279
|
|
278
280
|
def active_scaffold_select_name_with_multiple(options)
|
279
|
-
if options[:multiple]
|
280
|
-
|
281
|
-
end
|
281
|
+
return if !options[:multiple] || options[:name].to_s.ends_with?('[]')
|
282
|
+
options[:name] = "#{options[:name]}[]"
|
282
283
|
end
|
283
284
|
|
284
285
|
def active_scaffold_input_singular_association(column, html_options, options = {})
|
@@ -289,7 +290,7 @@ module ActiveScaffold
|
|
289
290
|
select_options.unshift(associated) unless associated.nil? || select_options.include?(associated)
|
290
291
|
|
291
292
|
method = column.name
|
292
|
-
options.merge! :selected => associated
|
293
|
+
options.merge! :selected => associated&.id, :include_blank => as_(:_select_), :object => record
|
293
294
|
|
294
295
|
html_options.merge!(column.options[:html_options] || {})
|
295
296
|
options.merge!(column.options)
|
@@ -303,10 +304,46 @@ module ActiveScaffold
|
|
303
304
|
collection_select(:record, method, select_options, :id, column.options[:label_method] || :to_label, options, html_options)
|
304
305
|
end
|
305
306
|
html << active_scaffold_refresh_link(column, html_options, record) if column.options[:refresh_link]
|
307
|
+
html << active_scaffold_new_record_subform(column, record, html_options) if column.options[:add_new]
|
306
308
|
html
|
307
309
|
end
|
308
310
|
|
309
|
-
def
|
311
|
+
def active_scaffold_new_record_subform(column, record, html_options, new_record_attributes: nil, locals: {}, skip_link: false) # rubocop:disable Metrics/ParameterLists
|
312
|
+
klass =
|
313
|
+
if column.association.polymorphic? && column.association.belongs_to?
|
314
|
+
type = record.send(column.association.foreign_type)
|
315
|
+
column.association.klass(record) if type.present? && (column.options[:add_new] == true || type.in?(column.options[:add_new]))
|
316
|
+
else
|
317
|
+
column.association.klass
|
318
|
+
end
|
319
|
+
return content_tag(:div, '') unless klass
|
320
|
+
subform_attrs = active_scaffold_subform_attributes(column, nil, klass)
|
321
|
+
if record.send(column.name)&.new_record?
|
322
|
+
new_record = record.send(column.name)
|
323
|
+
else
|
324
|
+
subform_attrs[:style] = 'display: none'
|
325
|
+
end
|
326
|
+
subform_attrs[:class] << ' optional'
|
327
|
+
scope = html_options[:name].scan(/record(.*)\[#{column.name}\]/).dig(0, 0)
|
328
|
+
new_record ||= klass.new(new_record_attributes)
|
329
|
+
locals = locals.reverse_merge(column: column, parent_record: record, associated: [], show_blank_record: new_record, scope: scope)
|
330
|
+
subform = render(partial: subform_partial_for_column(column, klass), locals: locals)
|
331
|
+
if column.options[:hide_subgroups]
|
332
|
+
toggable_id = "#{sub_form_id(association: column.name, id: record.id || generated_id(record) || 99_999_999_999)}-div"
|
333
|
+
subform << link_to_visibility_toggle(toggable_id, default_visible: false)
|
334
|
+
end
|
335
|
+
html = content_tag(:div, subform, subform_attrs)
|
336
|
+
return html if skip_link
|
337
|
+
html << active_scaffold_show_new_subform_link(column, record, html_options[:id], subform_attrs[:id])
|
338
|
+
end
|
339
|
+
|
340
|
+
def active_scaffold_show_new_subform_link(column, record, select_id, subform_id)
|
341
|
+
data = {select_id: select_id, subform_id: subform_id, subform_text: as_(:add_existing), select_text: as_(:create_new)}
|
342
|
+
label = data[record.send(column.name)&.new_record? ? :subform_text : :select_text]
|
343
|
+
link_to(label, '#', data: data, class: 'show-new-subform')
|
344
|
+
end
|
345
|
+
|
346
|
+
def active_scaffold_file_with_remove_link(column, options, content, remove_file_prefix, controls_class, &block) # rubocop:disable Metrics/ParameterLists
|
310
347
|
options = active_scaffold_input_text_options(options.merge(column.options))
|
311
348
|
if content
|
312
349
|
active_scaffold_file_with_content(column, content, options, remove_file_prefix, controls_class, &block)
|
@@ -329,12 +366,13 @@ module ActiveScaffold
|
|
329
366
|
object_name, method = options[:name].split(/\[(#{column.name})\]/)
|
330
367
|
method.sub!(/#{column.name}/, "#{remove_file_prefix}\\0")
|
331
368
|
fields = block_given? ? yield : ''
|
369
|
+
link_key = options[:multiple] ? :remove_files : :remove_file
|
332
370
|
input = file_field(:record, column.name, options.merge(:onchange => js_dont_remove_file_code))
|
333
371
|
content_tag(:div, class: controls_class) do
|
334
372
|
content_tag(:div) do
|
335
373
|
safe_join [content, ' | ', fields,
|
336
374
|
hidden_field(object_name, method, :value => 'false', class: 'remove_file'),
|
337
|
-
content_tag(:a, as_(
|
375
|
+
content_tag(:a, as_(link_key), :href => '#', :onclick => js_remove_file_code)]
|
338
376
|
end << content_tag(:div, input, :style => 'display: none')
|
339
377
|
end
|
340
378
|
end
|
@@ -345,7 +383,7 @@ module ActiveScaffold
|
|
345
383
|
link_options['data-update_send_form'] = html_options['data-update_send_form']
|
346
384
|
link_options['data-update_send_form_selector'] = html_options['data-update_send_form_selector']
|
347
385
|
else
|
348
|
-
scope = html_options[:name].scan(/^record((\[[^\]]*\])*)\[#{column.name}\]/)
|
386
|
+
scope = html_options[:name].scan(/^record((\[[^\]]*\])*)\[#{column.name}\]/).dig(0, 0) if html_options[:name]
|
349
387
|
link_options = update_columns_options(column, scope.presence, link_options, true)
|
350
388
|
end
|
351
389
|
link_options[:class] = 'refresh-link'
|
@@ -385,11 +423,11 @@ module ActiveScaffold
|
|
385
423
|
label_method = column.options[:label_method] || :to_label
|
386
424
|
html = hidden_field_tag("#{options[:name]}[]", '', :id => nil)
|
387
425
|
html << content_tag(:ul, options.merge(:class => "#{options[:class]} checkbox-list#{' draggable-lists' if column.options[:draggable_lists]}")) do
|
388
|
-
content =
|
426
|
+
content = []
|
389
427
|
select_options.each_with_index do |option, i|
|
390
428
|
content << active_scaffold_checkbox_option(option, label_method, associated_ids, :name => "#{options[:name]}[]", :id => "#{options[:id]}_#{i}_id")
|
391
429
|
end
|
392
|
-
content
|
430
|
+
safe_join content
|
393
431
|
end
|
394
432
|
html
|
395
433
|
end
|
@@ -418,9 +456,9 @@ module ActiveScaffold
|
|
418
456
|
end
|
419
457
|
|
420
458
|
def active_scaffold_input_select(column, html_options)
|
421
|
-
if column.association
|
459
|
+
if column.association&.singular?
|
422
460
|
active_scaffold_input_singular_association(column, html_options)
|
423
|
-
elsif column.association
|
461
|
+
elsif column.association&.collection?
|
424
462
|
active_scaffold_input_plural_association(column, html_options)
|
425
463
|
else
|
426
464
|
active_scaffold_input_enum(column, html_options)
|
@@ -430,7 +468,8 @@ module ActiveScaffold
|
|
430
468
|
def active_scaffold_radio_option(option, selected, column, radio_options)
|
431
469
|
if column.association
|
432
470
|
label_method = column.options[:label_method] || :to_label
|
433
|
-
text
|
471
|
+
text = option.send(label_method)
|
472
|
+
value = option.id
|
434
473
|
checked = {:checked => selected == value}
|
435
474
|
else
|
436
475
|
text, value = active_scaffold_translated_option(column, *option)
|
@@ -452,11 +491,36 @@ module ActiveScaffold
|
|
452
491
|
active_scaffold_enum_options(column, record)
|
453
492
|
end
|
454
493
|
|
455
|
-
selected = record.send(column.association.name)
|
456
|
-
|
457
|
-
|
494
|
+
selected = record.send(column.association.name) if column.association
|
495
|
+
selected_id = selected&.id
|
496
|
+
if options.present?
|
497
|
+
if column.options[:add_new]
|
498
|
+
html_options[:data] ||= {}
|
499
|
+
html_options[:data][:subform_id] = active_scaffold_subform_attributes(column)[:id]
|
500
|
+
radio_html_options = html_options.merge(class: html_options[:class] + ' hide-new-subform')
|
501
|
+
else
|
502
|
+
radio_html_options = html_options
|
503
|
+
end
|
504
|
+
radios = options.map do |option|
|
505
|
+
active_scaffold_radio_option(option, selected_id, column, radio_html_options)
|
506
|
+
end
|
507
|
+
if column.options[:include_blank]
|
508
|
+
label = column.options[:include_blank]
|
509
|
+
label = as_(column.options[:include_blank]) if column.options[:include_blank].is_a?(Symbol)
|
510
|
+
radios.prepend content_tag(:label, radio_button(:record, column.name, '', html_options.merge(id: nil)) + label)
|
511
|
+
end
|
512
|
+
if column.options[:add_new]
|
513
|
+
create_new_button = radio_button_tag(html_options[:name], '', selected&.new_record?, html_options.merge(id: nil, class: html_options[:class] + ' show-new-subform'))
|
514
|
+
radios << content_tag(:label, create_new_button << as_(:create_new)) <<
|
515
|
+
active_scaffold_new_record_subform(column, record, html_options, skip_link: true)
|
516
|
+
end
|
517
|
+
safe_join radios
|
518
|
+
else
|
519
|
+
html = content_tag(:span, as_(:no_options), :class => "#{html_options[:class]} no-options", :id => html_options[:id])
|
520
|
+
html << hidden_field_tag(html_options[:name], '', :id => nil)
|
521
|
+
html << active_scaffold_new_record_subform(column, record, html_options) if column.options[:add_new]
|
522
|
+
html
|
458
523
|
end
|
459
|
-
safe_join radios
|
460
524
|
end
|
461
525
|
|
462
526
|
def active_scaffold_input_checkbox(column, options)
|
@@ -464,7 +528,7 @@ module ActiveScaffold
|
|
464
528
|
end
|
465
529
|
|
466
530
|
def active_scaffold_input_password(column, options)
|
467
|
-
active_scaffold_text_input :password_field, column, options
|
531
|
+
active_scaffold_text_input :password_field, column, options.reverse_merge(autocomplete: 'new-password')
|
468
532
|
end
|
469
533
|
|
470
534
|
def active_scaffold_input_textarea(column, options)
|
@@ -518,16 +582,17 @@ module ActiveScaffold
|
|
518
582
|
|
519
583
|
# A color picker
|
520
584
|
def active_scaffold_input_color(column, options)
|
585
|
+
html = []
|
521
586
|
options = active_scaffold_input_text_options(options)
|
522
|
-
if column.column
|
587
|
+
if column.column&.null
|
523
588
|
no_color = options[:object].send(column.name).nil?
|
524
589
|
method = no_color ? :hidden_field : :color_field
|
525
|
-
html
|
590
|
+
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}")
|
526
591
|
else
|
527
592
|
method = :color_field
|
528
|
-
html = ''.html_safe
|
529
593
|
end
|
530
594
|
html << send(method, :record, column.name, options.merge(column.options).except(:format, :no_color))
|
595
|
+
safe_join html
|
531
596
|
end
|
532
597
|
|
533
598
|
#
|
@@ -538,8 +603,8 @@ module ActiveScaffold
|
|
538
603
|
record = options.delete(:object)
|
539
604
|
select_options = []
|
540
605
|
select_options << [as_(:_select_), nil] if !column.virtual? && column.column.null
|
541
|
-
select_options << [as_(:true), true]
|
542
|
-
select_options << [as_(:false), false]
|
606
|
+
select_options << [as_(:true), true] # rubocop:disable Lint/BooleanSymbol
|
607
|
+
select_options << [as_(:false), false] # rubocop:disable Lint/BooleanSymbol
|
543
608
|
|
544
609
|
select_tag(options[:name], options_for_select(select_options, record.send(column.name)), options)
|
545
610
|
end
|
@@ -600,8 +665,8 @@ module ActiveScaffold
|
|
600
665
|
end
|
601
666
|
alias override_input? override_input
|
602
667
|
|
603
|
-
def subform_partial_for_column(column)
|
604
|
-
subform_partial = "#{active_scaffold_config_for(column.association.klass).subform.layout}_subform"
|
668
|
+
def subform_partial_for_column(column, klass = nil)
|
669
|
+
subform_partial = "#{column.options[:layout] || active_scaffold_config_for(klass || column.association.klass).subform.layout}_subform"
|
605
670
|
override_subform_partial(column, subform_partial) || subform_partial
|
606
671
|
end
|
607
672
|
|
@@ -622,7 +687,7 @@ module ActiveScaffold
|
|
622
687
|
end
|
623
688
|
|
624
689
|
def column_scope(column, scope = nil, record = nil)
|
625
|
-
if column.association
|
690
|
+
if column.association&.collection?
|
626
691
|
"#{scope}[#{column.name}][#{record.id || generate_temporary_id(record)}]"
|
627
692
|
else
|
628
693
|
"#{scope}[#{column.name}]"
|
@@ -635,12 +700,12 @@ module ActiveScaffold
|
|
635
700
|
remote_controller = active_scaffold_controller_for(record_select_config.model).controller_path
|
636
701
|
options[:controller] = remote_controller
|
637
702
|
options.merge!(active_scaffold_input_text_options)
|
638
|
-
record_select_field(options[:name],
|
703
|
+
record_select_field(options[:name], nil, options)
|
639
704
|
else
|
640
705
|
select_options = sorted_association_options_find(nested.association, nil, record)
|
641
706
|
select_options ||= active_scaffold_config.model.all
|
642
707
|
select_options = options_from_collection_for_select(select_options, :id, :to_label)
|
643
|
-
select_tag 'associated_id', (
|
708
|
+
select_tag 'associated_id', (content_tag(:option, as_(:_select_), value: '') + select_options) unless select_options.empty?
|
644
709
|
end
|
645
710
|
end
|
646
711
|
|
@@ -653,55 +718,62 @@ module ActiveScaffold
|
|
653
718
|
end
|
654
719
|
|
655
720
|
# Try to get numerical constraints from model's validators
|
656
|
-
def
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
v.is_a?(ActiveModel::Validations::NumericalityValidator) && v.attributes.include?(column.name)
|
661
|
-
end
|
662
|
-
equal_to = (val = validators.find { |v| v.options[:equal_to] }) ? val.options[:equal_to] : nil
|
663
|
-
|
664
|
-
# If there is equal_to constraint - use it (unless otherwise specified by user)
|
665
|
-
if equal_to && !(options[:min] || options[:max])
|
666
|
-
numerical_constraints[:min] = numerical_constraints[:max] = equal_to
|
667
|
-
else # find minimum and maximum from validators
|
668
|
-
# we can safely modify :min and :max by 1 for :greater_tnan or :less_than value only for integer values
|
669
|
-
only_integer = column.column.type == :integer if column.column
|
670
|
-
only_integer ||= validators.find { |v| v.options[:only_integer] }.present?
|
671
|
-
margin = only_integer ? 1 : 0
|
672
|
-
|
673
|
-
# Minimum
|
674
|
-
unless options[:min]
|
675
|
-
min = validators.map { |v| v.options[:greater_than_or_equal] }.compact.max
|
676
|
-
greater_than = validators.map { |v| v.options[:greater_than] }.compact.max
|
677
|
-
numerical_constraints[:min] = [min, (greater_than + margin if greater_than)].compact.max
|
678
|
-
end
|
721
|
+
def column_numerical_constraints(column, options)
|
722
|
+
validators = column.active_record_class.validators.select do |v|
|
723
|
+
v.is_a?(ActiveModel::Validations::NumericalityValidator) && v.attributes.include?(column.name)
|
724
|
+
end
|
679
725
|
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
726
|
+
equal_validator = validators.find { |v| v.options[:equal_to] }
|
727
|
+
# If there is equal_to constraint - use it (unless otherwise specified by user)
|
728
|
+
if equal_validator && !(options[:min] || options[:max])
|
729
|
+
equal_to = equal_validator.options[:equal_to]
|
730
|
+
return {min: equal_to, max: equal_to}
|
731
|
+
end
|
686
732
|
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
733
|
+
numerical_constraints = {}
|
734
|
+
|
735
|
+
# find minimum and maximum from validators
|
736
|
+
# we can safely modify :min and :max by 1 for :greater_tnan or :less_than value only for integer values
|
737
|
+
only_integer = column.column.type == :integer if column.column
|
738
|
+
only_integer ||= validators.find { |v| v.options[:only_integer] }.present?
|
739
|
+
margin = only_integer ? 1 : 0
|
740
|
+
|
741
|
+
# Minimum
|
742
|
+
unless options[:min]
|
743
|
+
min = validators.map { |v| v.options[:greater_than_or_equal_to] }.compact.max
|
744
|
+
greater_than = validators.map { |v| v.options[:greater_than] }.compact.max
|
745
|
+
numerical_constraints[:min] = [min, (greater_than + margin if greater_than)].compact.max
|
746
|
+
end
|
747
|
+
|
748
|
+
# Maximum
|
749
|
+
unless options[:max]
|
750
|
+
max = validators.map { |v| v.options[:less_than_or_equal_to] }.compact.min
|
751
|
+
less_than = validators.map { |v| v.options[:less_than] }.compact.min
|
752
|
+
numerical_constraints[:max] = [max, (less_than - margin if less_than)].compact.min
|
753
|
+
end
|
754
|
+
|
755
|
+
# Set step = 2 for column values restricted to be odd or even (but only if minimum is set)
|
756
|
+
unless options[:step]
|
757
|
+
only_odd_valid = validators.any? { |v| v.options[:odd] }
|
758
|
+
only_even_valid = validators.any? { |v| v.options[:even] } unless only_odd_valid
|
759
|
+
if !only_integer
|
760
|
+
numerical_constraints[:step] ||= "0.#{'0' * (column.column.scale - 1)}1" if column.column&.scale.to_i.positive?
|
761
|
+
elsif options[:min] && options[:min].respond_to?(:even?) && (only_odd_valid || only_even_valid)
|
762
|
+
numerical_constraints[:step] = 2
|
763
|
+
numerical_constraints[:min] += 1 if only_odd_valid && options[:min].even?
|
764
|
+
numerical_constraints[:min] += 1 if only_even_valid && options[:min].odd?
|
700
765
|
end
|
766
|
+
numerical_constraints[:step] ||= 'any' unless only_integer
|
767
|
+
end
|
701
768
|
|
702
|
-
|
769
|
+
numerical_constraints
|
770
|
+
end
|
771
|
+
|
772
|
+
def numerical_constraints_for_column(column, options)
|
773
|
+
constraints = Rails.cache.fetch("#{column.cache_key}#numerical_constarints") do
|
774
|
+
column_numerical_constraints(column, options)
|
703
775
|
end
|
704
|
-
|
776
|
+
constraints.merge(options)
|
705
777
|
end
|
706
778
|
end
|
707
779
|
end
|