active_scaffold 4.0.13 → 4.1.0
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.rdoc +19 -0
- data/README.md +11 -5
- data/app/assets/javascripts/active_scaffold.js.erb +1 -1
- data/app/assets/javascripts/jquery/active_scaffold.js +67 -20
- data/app/assets/javascripts/jquery/date_picker_bridge.js.erb +1 -1
- data/app/assets/javascripts/jquery/draggable_lists.js +1 -1
- data/app/assets/javascripts/jquery/tiny_mce_bridge.js +1 -0
- data/app/assets/stylesheets/active_scaffold.scss +415 -6
- data/app/assets/stylesheets/active_scaffold_layout.css +11 -1
- data/app/views/active_scaffold_overrides/_form.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_form_association_record.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_human_filters.html.erb +1 -0
- data/app/views/active_scaffold_overrides/_list_header.html.erb +2 -0
- data/app/views/active_scaffold_overrides/_list_messages.html.erb +11 -0
- data/app/views/active_scaffold_overrides/_render_field.js.erb +1 -1
- data/app/views/active_scaffold_overrides/_show_association.html.erb +1 -0
- data/app/views/active_scaffold_overrides/_show_columns.html.erb +17 -2
- data/app/views/active_scaffold_overrides/_update_field_on_create.js.erb +20 -0
- data/app/views/active_scaffold_overrides/add_tab.js.erb +3 -3
- data/app/views/active_scaffold_overrides/edit_associated.js.erb +1 -1
- data/app/views/active_scaffold_overrides/on_create.js.erb +21 -17
- data/app/views/active_scaffold_overrides/on_update.js.erb +1 -1
- data/lib/active_scaffold/actions/core.rb +34 -16
- data/lib/active_scaffold/actions/field_search.rb +21 -8
- data/lib/active_scaffold/actions/list.rb +40 -5
- data/lib/active_scaffold/actions/nested.rb +1 -1
- data/lib/active_scaffold/actions/subform.rb +2 -1
- data/lib/active_scaffold/attribute_params.rb +12 -2
- data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/paper_trail/helper.rb +1 -1
- data/lib/active_scaffold/bridges/record_select/helpers.rb +1 -1
- data/lib/active_scaffold/config/core.rb +5 -1
- data/lib/active_scaffold/config/list.rb +35 -1
- data/lib/active_scaffold/constraints.rb +4 -2
- data/lib/active_scaffold/data_structures/action_columns.rb +2 -2
- data/lib/active_scaffold/data_structures/action_link.rb +16 -11
- data/lib/active_scaffold/data_structures/action_links.rb +3 -3
- data/lib/active_scaffold/data_structures/actions.rb +2 -2
- data/lib/active_scaffold/data_structures/column.rb +21 -0
- data/lib/active_scaffold/data_structures/columns.rb +2 -2
- data/lib/active_scaffold/data_structures/filter.rb +85 -0
- data/lib/active_scaffold/data_structures/filter_option.rb +32 -0
- data/lib/active_scaffold/data_structures/filters.rb +51 -0
- data/lib/active_scaffold/data_structures/set.rb +2 -2
- data/lib/active_scaffold/data_structures/sorting.rb +2 -2
- data/lib/active_scaffold/extensions/action_controller_rendering.rb +2 -2
- data/lib/active_scaffold/extensions/action_view_rendering.rb +3 -3
- data/lib/active_scaffold/finder.rb +16 -6
- data/lib/active_scaffold/helpers/action_link_helpers.rb +21 -17
- data/lib/active_scaffold/helpers/filter_helpers.rb +36 -0
- data/lib/active_scaffold/helpers/form_column_helpers.rb +116 -26
- data/lib/active_scaffold/helpers/human_condition_helpers.rb +4 -0
- data/lib/active_scaffold/helpers/list_column_helpers.rb +26 -12
- data/lib/active_scaffold/helpers/search_column_helpers.rb +4 -2
- data/lib/active_scaffold/helpers/show_column_helpers.rb +4 -3
- data/lib/active_scaffold/helpers/tabs_helpers.rb +4 -3
- data/lib/active_scaffold/helpers/view_helpers.rb +7 -1
- data/lib/active_scaffold/registry.rb +1 -1
- data/lib/active_scaffold/responds_to_parent.rb +3 -3
- data/lib/active_scaffold/tableless.rb +2 -2
- data/lib/active_scaffold/version.rb +2 -2
- data/lib/active_scaffold.rb +3 -7
- metadata +22 -17
- data/app/assets/stylesheets/active_scaffold_colors.scss +0 -414
@@ -0,0 +1,85 @@
|
|
1
|
+
module ActiveScaffold::DataStructures
|
2
|
+
class Filter
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :name, :default_option
|
6
|
+
attr_writer :label, :description
|
7
|
+
attr_accessor :type, :weight, :css_class, :security_method
|
8
|
+
|
9
|
+
def initialize(name, type)
|
10
|
+
raise ArgumentError, 'Filter name must use only word characters (a-zA-Z0-9_)' unless name.match?(/\A\w+\z/)
|
11
|
+
|
12
|
+
@label = @name = name.to_sym
|
13
|
+
@type = type
|
14
|
+
@options = []
|
15
|
+
@weight = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
# adds a FilterOption, creating one from the arguments if need be
|
19
|
+
def add(name, options = {})
|
20
|
+
if name.is_a?(ActiveScaffold::DataStructures::FilterOption)
|
21
|
+
option = name
|
22
|
+
name = option.name
|
23
|
+
end
|
24
|
+
existing = self[name]
|
25
|
+
raise ArgumentError, "there is a filter option with '#{name}' name" if existing
|
26
|
+
|
27
|
+
option ||= ActiveScaffold::DataStructures::FilterOption.new(@name, name, options)
|
28
|
+
@default_option ||= option.name
|
29
|
+
@options << option
|
30
|
+
self
|
31
|
+
end
|
32
|
+
alias << add
|
33
|
+
|
34
|
+
def default_option=(name)
|
35
|
+
option = self[name]
|
36
|
+
raise ArgumentError, "'#{name}' option not found" unless option
|
37
|
+
|
38
|
+
@default_option = option.name
|
39
|
+
end
|
40
|
+
|
41
|
+
# finds a FilterOption by matching the name
|
42
|
+
def [](option_name)
|
43
|
+
@options.find { |option| option.name.to_s == option_name.to_s }
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete(option_name)
|
47
|
+
@options.delete self[option_name]
|
48
|
+
end
|
49
|
+
|
50
|
+
# iterates over the links, possibly by type
|
51
|
+
def each(&)
|
52
|
+
@options.each(&)
|
53
|
+
end
|
54
|
+
|
55
|
+
def empty?
|
56
|
+
@options.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
def label(*)
|
60
|
+
case @label
|
61
|
+
when Symbol
|
62
|
+
ActiveScaffold::Registry.cache(:translations, @label) { as_(@label) }
|
63
|
+
else
|
64
|
+
@label
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def description
|
69
|
+
case @description
|
70
|
+
when Symbol
|
71
|
+
ActiveScaffold::Registry.cache(:translations, @description) { as_(@description) }
|
72
|
+
else
|
73
|
+
@description
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
# called during clone or dup. makes the clone/dup deeper.
|
80
|
+
def initialize_copy(from)
|
81
|
+
@options = []
|
82
|
+
from.each { |option| @options << option.clone }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActiveScaffold::DataStructures
|
2
|
+
class FilterOption < ActionLink
|
3
|
+
attr_reader :name, :filter_name
|
4
|
+
attr_writer :description
|
5
|
+
attr_accessor :conditions
|
6
|
+
|
7
|
+
def initialize(filter_name, name, options = {})
|
8
|
+
@filter_name = filter_name
|
9
|
+
@label = @name = name.to_sym
|
10
|
+
super(
|
11
|
+
:index,
|
12
|
+
options.merge(
|
13
|
+
action: :index,
|
14
|
+
type: :collection,
|
15
|
+
method: :get,
|
16
|
+
position: false,
|
17
|
+
toggle: true
|
18
|
+
)
|
19
|
+
)
|
20
|
+
parameters.merge!(filter_name => name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def description
|
24
|
+
case @description
|
25
|
+
when Symbol
|
26
|
+
ActiveScaffold::Registry.cache(:translations, @description) { as_(@description) }
|
27
|
+
else
|
28
|
+
@description
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ActiveScaffold::DataStructures
|
2
|
+
class Filters
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@set = []
|
7
|
+
@default_type = self.class.default_type
|
8
|
+
end
|
9
|
+
|
10
|
+
# adds a FilterOption, creating one from the arguments if need be
|
11
|
+
def add(name, &block)
|
12
|
+
if name.is_a?(ActiveScaffold::DataStructures::Filter)
|
13
|
+
filter = name
|
14
|
+
name = filter.name
|
15
|
+
end
|
16
|
+
existing = self[name]
|
17
|
+
raise ArgumentError, "there is a filter with '#{name}' name" if existing
|
18
|
+
|
19
|
+
filter ||= ActiveScaffold::DataStructures::Filter.new(name, default_type)
|
20
|
+
@set << filter
|
21
|
+
block&.call filter
|
22
|
+
self
|
23
|
+
end
|
24
|
+
alias << add
|
25
|
+
|
26
|
+
# finds a Filter by matching the name
|
27
|
+
def [](name)
|
28
|
+
@set.find { |filter| filter.name.to_s == name.to_s }
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete(name)
|
32
|
+
@set.delete self[name]
|
33
|
+
end
|
34
|
+
|
35
|
+
# iterates over the links, possibly by type
|
36
|
+
def each(&)
|
37
|
+
@set.each(&)
|
38
|
+
end
|
39
|
+
|
40
|
+
def empty?
|
41
|
+
@set.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
# default filter type for all app filters, can be :links or :select
|
45
|
+
cattr_accessor :default_type
|
46
|
+
@@default_type = :links
|
47
|
+
|
48
|
+
# default filter type for all filters in this set, can be :links or :select
|
49
|
+
attr_accessor :default_type
|
50
|
+
end
|
51
|
+
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# wrap the action rendering for ActiveScaffold controllers
|
2
2
|
module ActiveScaffold
|
3
3
|
module ActionController # :nodoc:
|
4
|
-
def render(*args, &
|
4
|
+
def render(*args, &)
|
5
5
|
if self.class.uses_active_scaffold? && params[:adapter] && @rendering_adapter.nil? && request.xhr?
|
6
6
|
@rendering_adapter = true # recursion control
|
7
7
|
# if we need an adapter, then we render the actual stuff to a string and insert it into the adapter template
|
8
8
|
opts = args.any? ? args.first : {}
|
9
9
|
|
10
10
|
render partial: params[:adapter][1..],
|
11
|
-
locals: {payload: render_to_string(opts.merge(layout: false), &
|
11
|
+
locals: {payload: render_to_string(opts.merge(layout: false), &).html_safe}, # rubocop:disable Rails/OutputSafety
|
12
12
|
use_full_path: true, layout: false, content_type: :html
|
13
13
|
@rendering_adapter = nil # recursion control
|
14
14
|
else
|
@@ -35,7 +35,7 @@ module ActiveScaffold # :nodoc:
|
|
35
35
|
#
|
36
36
|
# options[:xhr] force to load embedded scaffold with AJAX even when render_component gem is installed.
|
37
37
|
#
|
38
|
-
def render(*args, &
|
38
|
+
def render(*args, &)
|
39
39
|
if args.first.is_a?(Hash) && args.first[:active_scaffold]
|
40
40
|
render_embedded args.first
|
41
41
|
elsif args.first == :super
|
@@ -103,9 +103,9 @@ module ActiveScaffold # :nodoc:
|
|
103
103
|
def update_view_paths
|
104
104
|
last_view_path =
|
105
105
|
if @lookup_context # rails 6
|
106
|
-
File.expand_path(File.dirname(
|
106
|
+
File.expand_path(File.dirname(@lookup_context.last_template.short_identifier.to_s, 2), Rails.root)
|
107
107
|
else
|
108
|
-
File.expand_path(File.dirname(
|
108
|
+
File.expand_path(File.dirname(lookup_context.last_template.inspect, 2), Rails.root)
|
109
109
|
end
|
110
110
|
new_view_paths = view_paths.drop(view_paths.find_index { |path| path.to_s == last_view_path } + 1)
|
111
111
|
if @lookup_context # rails 6
|
@@ -502,7 +502,7 @@ module ActiveScaffold
|
|
502
502
|
|
503
503
|
protected
|
504
504
|
|
505
|
-
attr_writer :active_scaffold_conditions, :active_scaffold_preload, :
|
505
|
+
attr_writer :active_scaffold_conditions, :active_scaffold_preload, :active_scaffold_joins, :active_scaffold_outer_joins, :active_scaffold_references
|
506
506
|
|
507
507
|
def active_scaffold_conditions
|
508
508
|
@active_scaffold_conditions ||= []
|
@@ -512,8 +512,18 @@ module ActiveScaffold
|
|
512
512
|
@active_scaffold_preload ||= []
|
513
513
|
end
|
514
514
|
|
515
|
+
def active_scaffold_joins
|
516
|
+
@active_scaffold_joins ||= []
|
517
|
+
end
|
518
|
+
|
515
519
|
def active_scaffold_habtm_joins
|
516
|
-
|
520
|
+
ActiveScaffold.deprecator.warn 'use active_scaffold_joins'
|
521
|
+
active_scaffold_joins
|
522
|
+
end
|
523
|
+
|
524
|
+
def active_scaffold_habtm_joins=(value)
|
525
|
+
ActiveScaffold.deprecator.warn 'use active_scaffold_joins='
|
526
|
+
self.active_scaffold_joins = value
|
517
527
|
end
|
518
528
|
|
519
529
|
def active_scaffold_outer_joins
|
@@ -559,7 +569,7 @@ module ActiveScaffold
|
|
559
569
|
# returns a single record (the given id) but only if it's allowed for the specified security options.
|
560
570
|
# security options can be a hash for authorized_for? method or a value to check as a :crud_type
|
561
571
|
# accomplishes this by checking model.#{action}_authorized?
|
562
|
-
def find_if_allowed(id, security_options, klass =
|
572
|
+
def find_if_allowed(id, security_options, klass = filtered_query)
|
563
573
|
record = klass.find(id)
|
564
574
|
security_options = {crud_type: security_options.to_sym} unless security_options.is_a? Hash
|
565
575
|
raise ActiveScaffold::RecordNotAllowed, "#{klass} with id = #{id}" unless record.authorized_for? security_options
|
@@ -623,7 +633,7 @@ module ActiveScaffold
|
|
623
633
|
options[:page] ||= 1
|
624
634
|
|
625
635
|
find_options = finder_options(options)
|
626
|
-
query =
|
636
|
+
query = filtered_query
|
627
637
|
query = query.where(nil) if active_scaffold_config.active_record? # where(nil) is needed because we need a relation
|
628
638
|
|
629
639
|
# NOTE: we must use :include in the count query, because some conditions may reference other tables
|
@@ -663,7 +673,7 @@ module ActiveScaffold
|
|
663
673
|
left_joins = active_scaffold_outer_joins
|
664
674
|
left_joins += includes if includes
|
665
675
|
primary_key = active_scaffold_config.primary_key
|
666
|
-
subquery = append_to_query(
|
676
|
+
subquery = append_to_query(filtered_query, conditions: conditions, joins: joins_for_finder, left_joins: left_joins, select: active_scaffold_config.columns[primary_key].field)
|
667
677
|
subquery.unscope(:order)
|
668
678
|
end
|
669
679
|
|
@@ -690,7 +700,7 @@ module ActiveScaffold
|
|
690
700
|
joins_for_collection
|
691
701
|
else
|
692
702
|
[]
|
693
|
-
end +
|
703
|
+
end + active_scaffold_joins
|
694
704
|
end
|
695
705
|
|
696
706
|
def apply_conditions(relation, *conditions)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module ActiveScaffold
|
2
2
|
module Helpers
|
3
|
-
#
|
4
|
-
# Also a dumping ground for uncategorized helpers.
|
3
|
+
# Helpers rendering action links
|
5
4
|
module ActionLinkHelpers
|
6
5
|
# params which mustn't be copying to nested links
|
7
6
|
NESTED_PARAMS = %i[eid embedded association parent_scaffold].freeze
|
@@ -62,9 +61,9 @@ module ActiveScaffold
|
|
62
61
|
output
|
63
62
|
end
|
64
63
|
|
65
|
-
def display_action_link_group(link, record, options, &
|
64
|
+
def display_action_link_group(link, record, options, &)
|
66
65
|
options[:level] += 1
|
67
|
-
content = display_action_links(link, record, options, &
|
66
|
+
content = display_action_links(link, record, options, &)
|
68
67
|
options[:level] -= 1
|
69
68
|
display_action_link(link, content, record, options).tap { options[:first_action] = false } if content.present?
|
70
69
|
end
|
@@ -85,7 +84,7 @@ module ActiveScaffold
|
|
85
84
|
group_tag = :li
|
86
85
|
end
|
87
86
|
content = content_tag(group_tag, class: html_classes.presence, onclick: ('' if hover_via_click?)) do
|
88
|
-
content_tag(:div,
|
87
|
+
content_tag(:div, link.label(record), class: link.css_class, title: options[:title]) << content_tag(:ul, content)
|
89
88
|
end
|
90
89
|
else
|
91
90
|
content = render_action_link(link, record, options)
|
@@ -341,14 +340,23 @@ module ActiveScaffold
|
|
341
340
|
end
|
342
341
|
|
343
342
|
def action_link_selected?(link, record)
|
344
|
-
|
345
|
-
|
346
|
-
|
343
|
+
if link.respond_to?(:filter_name)
|
344
|
+
if params[link.filter_name]
|
345
|
+
params[link.filter_name].to_s == link.name.to_s
|
346
|
+
else
|
347
|
+
active_scaffold_config.list.filters[link.filter_name].default_option == link.name
|
348
|
+
end
|
349
|
+
else
|
350
|
+
missing_options, url_options = replaced_action_link_url_options(link, record)
|
351
|
+
safe_params = params.to_unsafe_h
|
352
|
+
(url_options - safe_params.to_a).blank? && missing_options.all? { |k, _| params[k].nil? }
|
353
|
+
end
|
347
354
|
end
|
348
355
|
|
349
356
|
def action_link_html_options(link, record, options)
|
350
357
|
link_id = get_action_link_id(link, record)
|
351
|
-
html_options =
|
358
|
+
html_options = options[:html_options] || link.html_options
|
359
|
+
html_options = html_options.merge(class: [html_options[:class], link.action.to_s].compact.join(' '))
|
352
360
|
html_options[:link] = action_link_text(link, record, options)
|
353
361
|
|
354
362
|
# Needs to be in html_options to as the adding _method to the url is no longer supported by Rails
|
@@ -363,6 +371,10 @@ module ActiveScaffold
|
|
363
371
|
html_options[:data][:action] = link.action
|
364
372
|
html_options[:data][:cancel_refresh] = true if link.refresh_on_close
|
365
373
|
html_options[:data][:keep_open] = true if link.keep_open?
|
374
|
+
if link.prompt?
|
375
|
+
html_options[:data][:prompt] = link.prompt(h(record&.to_label))
|
376
|
+
html_options[:data][:prompt_required] = true if link.prompt_required?
|
377
|
+
end
|
366
378
|
html_options[:remote] = true
|
367
379
|
end
|
368
380
|
|
@@ -376,14 +388,6 @@ module ActiveScaffold
|
|
376
388
|
html_options[:rel] = [html_options[:rel], 'noopener noreferrer'].compact.join(' ')
|
377
389
|
end
|
378
390
|
html_options[:id] = link_id
|
379
|
-
if link.dhtml_confirm?
|
380
|
-
unless link.inline?
|
381
|
-
html_options[:class] << ' as_action'
|
382
|
-
html_options[:page_link] = 'true'
|
383
|
-
end
|
384
|
-
html_options[:dhtml_confirm] = link.dhtml_confirm.value
|
385
|
-
html_options[:onclick] = link.dhtml_confirm.onclick_function(controller, link_id)
|
386
|
-
end
|
387
391
|
html_options
|
388
392
|
end
|
389
393
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ActiveScaffold
|
2
|
+
module Helpers
|
3
|
+
# Helpers rendering filters
|
4
|
+
module FilterHelpers
|
5
|
+
def clear_filters_params
|
6
|
+
active_scaffold_config.list.filters.each_with_object({}) do |filter, url_options|
|
7
|
+
url_options[filter.name] = nil
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def display_filters(filters)
|
12
|
+
content = filters.sort_by(&:weight).map { |filter| display_filter(filter) }
|
13
|
+
content_tag :div, safe_join(content), class: 'filters' if content.present?
|
14
|
+
end
|
15
|
+
|
16
|
+
def display_filter(filter)
|
17
|
+
return if filter.security_method && !controller.send(filter.security_method)
|
18
|
+
|
19
|
+
options = filter.reject { |option| option.security_method_set? && !controller.send(option.security_method) }
|
20
|
+
send :"display_filter_as_#{filter.type}", filter, options if options.present?
|
21
|
+
end
|
22
|
+
|
23
|
+
def display_filter_as_links(filter, options)
|
24
|
+
content = options.map { |option| display_action_link(option, nil, nil, authorized: true, level: 1) }
|
25
|
+
display_action_link(filter, safe_join(content), nil, level: 0, title: filter.description) if content.present?
|
26
|
+
end
|
27
|
+
|
28
|
+
def display_filter_as_select(filter, options)
|
29
|
+
content = options.map do |option|
|
30
|
+
content_tag :option, option.label(nil), data: {url: action_link_url(option, nil)}, selected: action_link_selected?(option, nil), title: option.description
|
31
|
+
end
|
32
|
+
select_tag nil, safe_join(content), class: "action_group #{link.css_class}", title: filter.description || filter.label, data: {remote: :url} if content.present?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -129,7 +129,7 @@ module ActiveScaffold
|
|
129
129
|
form_columns ||= @main_columns.visible_columns_names
|
130
130
|
end
|
131
131
|
form_columns ||= current_form_columns(record, scope, subform_controller)
|
132
|
-
if force || (form_columns && column.update_columns
|
132
|
+
if force || (form_columns && column.update_columns&.intersect?(form_columns))
|
133
133
|
url_params.reverse_merge! params_for(action: 'render_field', column: column.name, id: record.to_param)
|
134
134
|
if nested? && scope
|
135
135
|
url_params[:nested] = url_params.slice(:parent_scaffold, :association, nested.param_name)
|
@@ -146,6 +146,7 @@ module ActiveScaffold
|
|
146
146
|
options['data-update_url'] = url_for(url_params)
|
147
147
|
options['data-update_send_form'] = column.send_form_on_update_column
|
148
148
|
options['data-update_send_form_selector'] = column.options[:send_form_selector] if column.options[:send_form_selector]
|
149
|
+
options['data-skip-disable-form'] = !column.disable_on_update_column
|
149
150
|
end
|
150
151
|
options
|
151
152
|
end
|
@@ -332,8 +333,9 @@ module ActiveScaffold
|
|
332
333
|
record = html_options.delete(:object)
|
333
334
|
associated = html_options.include?(:associated) ? html_options.delete(:associated) : record.send(column.association.name)
|
334
335
|
|
335
|
-
|
336
|
-
select_options
|
336
|
+
helper_method = association_helper_method(column.association, :sorted_association_options_find)
|
337
|
+
select_options = send(helper_method, column.association, nil, record)
|
338
|
+
select_options.unshift(associated) if associated&.persisted? && select_options.exclude?(associated)
|
337
339
|
|
338
340
|
method = column.name
|
339
341
|
options.merge! selected: associated&.id, include_blank: as_(:_select_), object: record
|
@@ -350,21 +352,80 @@ module ActiveScaffold
|
|
350
352
|
collection_select(:record, method, select_options, :id, ui_options[:label_method] || :to_label, options, html_options)
|
351
353
|
end
|
352
354
|
html << active_scaffold_refresh_link(column, html_options, record, ui_options) if ui_options[:refresh_link]
|
353
|
-
html <<
|
355
|
+
html << active_scaffold_add_new(column, record, html_options, ui_options: ui_options) if ui_options[:add_new]
|
354
356
|
html
|
355
357
|
end
|
356
358
|
|
357
|
-
def
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
359
|
+
def active_scaffold_new_record_klass(column, record, **options)
|
360
|
+
if column.association.polymorphic? && column.association.belongs_to?
|
361
|
+
type = record.send(column.association.foreign_type)
|
362
|
+
type_options = options[:types]
|
363
|
+
column.association.klass(record) if type.present? && (type_options.nil? || type_options.include?(type))
|
364
|
+
else
|
365
|
+
column.association.klass
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
def active_scaffold_add_new(column, record, html_options, ui_options: column.options, skip_link: false)
|
370
|
+
options = ui_options[:add_new] == true ? {} : ui_options[:add_new]
|
371
|
+
if options.is_a?(Array)
|
372
|
+
ActiveScaffold.deprecator.warn "use add_new: {types: #{options.inspect}} instead of add_new: #{options.inspect}"
|
373
|
+
options = {types: options}
|
374
|
+
end
|
375
|
+
if ui_options[:hide_subgroups] && !options.key?(:hide_subgroups)
|
376
|
+
ActiveScaffold.deprecator.warn "use add_new: {hide_subgroups: #{ui_options[:hide_subgroups]}} instead of hide_subgroups: #{ui_options[:hide_subgroups]}"
|
377
|
+
options[:hide_subgroups] = ui_options[:hide_subgroups]
|
378
|
+
end
|
379
|
+
if ui_options[:layout] && !options.key?(:layout)
|
380
|
+
ActiveScaffold.deprecator.warn "use add_new: {layout: #{ui_options[:layout]}} instead of layout: #{ui_options[:layout]}"
|
381
|
+
options[:layout] = ui_options[:layout]
|
382
|
+
end
|
383
|
+
|
384
|
+
case options[:mode]
|
385
|
+
when nil, :subform
|
386
|
+
active_scaffold_new_record_subform(column, record, html_options, options: options, skip_link: skip_link)
|
387
|
+
when :popup
|
388
|
+
active_scaffold_new_record_popup(column, record, html_options, options: options) unless skip_link
|
389
|
+
else
|
390
|
+
raise ArgumentError, "unsupported mode for add_new: #{options[:mode].inspect}"
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def active_scaffold_new_record_url_options(column, record)
|
395
|
+
if column.association.reverse
|
396
|
+
constraint = [record.id]
|
397
|
+
constraint.unshift record.class.name if column.association.reverse_association.polymorphic?
|
398
|
+
{embedded: {constraints: {column.association.reverse => constraint}}}
|
399
|
+
else
|
400
|
+
raise "can't add constraint to create new record with :popup, no reverse association for " \
|
401
|
+
"\"#{column.name}\" in #{column.association.klass}, add the reverse association " \
|
402
|
+
'or override active_scaffold_new_record_url_options helper.'
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def active_scaffold_new_record_popup(column, record, html_options, options: {})
|
407
|
+
klass = send(override_helper_per_model(:active_scaffold_new_record_klass, record.class), column, record, **options)
|
408
|
+
klass = nil if options[:security_method] && !controller.send(options[:security_method])
|
409
|
+
klass = nil if klass && options[:security_method].nil? && !klass.authorized_for?(crud_type: :create)
|
410
|
+
return h('') unless klass
|
411
|
+
|
412
|
+
link_text = active_scaffold_add_new_text(options, :add_new_text, :add)
|
413
|
+
url_options_helper = override_helper_per_model(:active_scaffold_new_record_url_options, record.class)
|
414
|
+
url_options = send(url_options_helper, column, record)
|
415
|
+
url_options[:controller] ||= active_scaffold_controller_for(klass).controller_path
|
416
|
+
url_options[:action] ||= :new
|
417
|
+
url_options[:from_field] ||= html_options[:id]
|
418
|
+
url_options[:parent_model] ||= record.class.name
|
419
|
+
url_options[:parent_column] ||= column.name
|
420
|
+
url_options.reverse_merge! options[:url_options] if options[:url_options]
|
421
|
+
link_to(link_text, url_options, remote: true, data: {position: :popup}, class: 'as_action')
|
422
|
+
end
|
423
|
+
|
424
|
+
def active_scaffold_new_record_subform(column, record, html_options, options: {}, new_record_attributes: nil, locals: {}, skip_link: false)
|
425
|
+
klass = send(override_helper_per_model(:active_scaffold_new_record_klass, record.class), column, record, **options)
|
365
426
|
return content_tag(:div, '') unless klass
|
366
427
|
|
367
|
-
subform_attrs = active_scaffold_subform_attributes(column, nil, klass, ui_options:
|
428
|
+
subform_attrs = active_scaffold_subform_attributes(column, nil, klass, ui_options: options)
|
368
429
|
if record.send(column.name)&.new_record?
|
369
430
|
new_record = record.send(column.name)
|
370
431
|
else
|
@@ -374,19 +435,28 @@ module ActiveScaffold
|
|
374
435
|
scope = html_options[:name].scan(/record(.*)\[#{column.name}\]/).dig(0, 0)
|
375
436
|
new_record ||= klass.new(new_record_attributes)
|
376
437
|
locals = locals.reverse_merge(column: column, parent_record: record, associated: [], show_blank_record: new_record, scope: scope)
|
377
|
-
subform = render(partial: subform_partial_for_column(column, klass, ui_options:
|
378
|
-
if
|
438
|
+
subform = render(partial: subform_partial_for_column(column, klass, ui_options: options), locals: locals)
|
439
|
+
if options[:hide_subgroups]
|
379
440
|
toggable_id = "#{sub_form_id(association: column.name, id: record.id || generated_id(record) || 99_999_999_999)}-div"
|
380
441
|
subform << link_to_visibility_toggle(toggable_id, default_visible: false)
|
381
442
|
end
|
382
443
|
html = content_tag(:div, subform, subform_attrs)
|
383
444
|
return html if skip_link
|
384
445
|
|
385
|
-
html << active_scaffold_show_new_subform_link(column, record, html_options[:id], subform_attrs[:id])
|
446
|
+
html << active_scaffold_show_new_subform_link(column, record, html_options[:id], subform_attrs[:id], options: options)
|
447
|
+
end
|
448
|
+
|
449
|
+
def active_scaffold_add_new_text(options, key, default)
|
450
|
+
text = options[key] unless options == true
|
451
|
+
return text if text.is_a? String
|
452
|
+
|
453
|
+
as_(text || default)
|
386
454
|
end
|
387
455
|
|
388
|
-
def active_scaffold_show_new_subform_link(column, record, select_id, subform_id)
|
389
|
-
|
456
|
+
def active_scaffold_show_new_subform_link(column, record, select_id, subform_id, options: {})
|
457
|
+
add_existing = active_scaffold_add_new_text(options, :add_existing_text, :add_existing)
|
458
|
+
create_new = active_scaffold_add_new_text(options, :add_new_text, :create_new)
|
459
|
+
data = {select_id: select_id, subform_id: subform_id, subform_text: add_existing, select_text: create_new}
|
390
460
|
label = data[record.send(column.name)&.new_record? ? :subform_text : :select_text]
|
391
461
|
link_to(label, '#', data: data, class: 'show-new-subform')
|
392
462
|
end
|
@@ -439,7 +509,8 @@ module ActiveScaffold
|
|
439
509
|
|
440
510
|
def active_scaffold_plural_association_options(column, record = nil)
|
441
511
|
associated_options = record.send(column.association.name)
|
442
|
-
|
512
|
+
helper_method = association_helper_method(column.association, :sorted_association_options_find)
|
513
|
+
[associated_options, associated_options | send(helper_method, column.association, nil, record)]
|
443
514
|
end
|
444
515
|
|
445
516
|
def active_scaffold_input_plural_association(column, options, ui_options: column.options)
|
@@ -557,7 +628,8 @@ module ActiveScaffold
|
|
557
628
|
html_options.merge!(ui_options[:html_options] || {})
|
558
629
|
options =
|
559
630
|
if column.association
|
560
|
-
|
631
|
+
helper_method = association_helper_method(column.association, :sorted_association_options_find)
|
632
|
+
send(helper_method, column.association, nil, record)
|
561
633
|
else
|
562
634
|
enum_options_method = override_helper_per_model(:active_scaffold_enum_options, record.class)
|
563
635
|
send(enum_options_method, column, record, ui_options: ui_options)
|
@@ -579,18 +651,35 @@ module ActiveScaffold
|
|
579
651
|
if ui_options[:include_blank]
|
580
652
|
label = ui_options[:include_blank]
|
581
653
|
label = as_(ui_options[:include_blank]) if ui_options[:include_blank].is_a?(Symbol)
|
582
|
-
|
654
|
+
radio_id = "#{html_options[:id]}-"
|
655
|
+
radios.prepend content_tag(:label, radio_button(:record, column.name, '', html_options.merge(id: radio_id)) + label)
|
583
656
|
end
|
584
657
|
if ui_options[:add_new]
|
585
|
-
|
586
|
-
|
587
|
-
|
658
|
+
if ui_options[:add_new] == true || ui_options[:add_new][:mode].in?([nil, :subform])
|
659
|
+
create_new = content_tag(:label) do
|
660
|
+
radio_button_tag(html_options[:name], '', selected&.new_record?, html_options.merge(
|
661
|
+
id: "#{html_options[:id]}-create_new", class: "#{html_options[:class]} show-new-subform"
|
662
|
+
).except(:object)) <<
|
663
|
+
active_scaffold_add_new_text(ui_options[:add_new], :add_new_text, :create_new)
|
664
|
+
end
|
665
|
+
radios << create_new
|
666
|
+
skip_link = true
|
667
|
+
else
|
668
|
+
ui_options = ui_options.merge(add_new: ui_options[:add_new].merge(
|
669
|
+
url_options: {
|
670
|
+
parent_scope: html_options[:name].gsub(/^record|\[[^\]]*\]$/, '').presence,
|
671
|
+
radio_data: html_options.slice(*html_options.keys.grep(/^data-update_/))
|
672
|
+
}
|
673
|
+
))
|
674
|
+
radios << content_tag(:span, '', class: 'new-radio-container', id: html_options[:id])
|
675
|
+
end
|
676
|
+
radios << active_scaffold_add_new(column, record, html_options, ui_options: ui_options, skip_link: skip_link)
|
588
677
|
end
|
589
678
|
safe_join radios
|
590
679
|
else
|
591
680
|
html = content_tag(:span, as_(:no_options), class: "#{html_options[:class]} no-options", id: html_options[:id])
|
592
681
|
html << hidden_field_tag(html_options[:name], '', id: nil)
|
593
|
-
html <<
|
682
|
+
html << active_scaffold_add_new(column, record, html_options, ui_options: ui_options) if ui_options[:add_new]
|
594
683
|
html
|
595
684
|
end
|
596
685
|
end
|
@@ -778,7 +867,8 @@ module ActiveScaffold
|
|
778
867
|
options.merge!(active_scaffold_input_text_options)
|
779
868
|
record_select_field(options[:name], nil, options)
|
780
869
|
else
|
781
|
-
|
870
|
+
helper_method = association_helper_method(column.association, :sorted_association_options_find)
|
871
|
+
select_options = send(helper_method, nested.association, nil, record)
|
782
872
|
select_options ||= active_scaffold_config.model.all
|
783
873
|
select_options = options_from_collection_for_select(select_options, :id, :to_label)
|
784
874
|
select_tag 'associated_id', (content_tag(:option, as_(:_select_), value: '') + select_options) unless select_options.empty?
|
@@ -16,6 +16,10 @@ module ActiveScaffold
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
def active_scaffold_human_filter_for(filter_option)
|
20
|
+
filter_option.label
|
21
|
+
end
|
22
|
+
|
19
23
|
def active_scaffold_grouped_by_label
|
20
24
|
text, = active_scaffold_config.field_search.group_options.find do |text, value|
|
21
25
|
(value || text).to_s == field_search_params['active_scaffold_group']
|