active_scaffold 3.5.3 → 3.6.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{CHANGELOG → CHANGELOG.rdoc} +73 -0
- data/README.md +17 -7
- data/app/assets/javascripts/active_scaffold.js.erb +0 -1
- data/app/assets/javascripts/jquery/active_scaffold.js +97 -6
- 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 +9 -7
- 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 +1 -0
- data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
- data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +9 -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 +11 -9
- 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 +34 -14
- 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 +21 -19
- 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 +86 -57
- data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
- data/lib/active_scaffold/helpers/search_column_helpers.rb +29 -34
- data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
- data/lib/active_scaffold/helpers/view_helpers.rb +38 -35
- 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 +67 -65
- 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 +1 -1
- 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/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 +2 -2
- 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 +6 -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 +3 -1
- 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 +10 -2
- metadata +55 -20
- 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
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
32
|
+
if column&.includes
|
33
|
+
include_assoc = column.includes.find { |assoc| assoc.is_a?(Hash) && assoc.include?(association.name) }
|
34
|
+
relation = relation.includes(include_assoc[association.name]) if include_assoc
|
35
|
+
end
|
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] }.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] }.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
|