active_scaffold 3.0.23 → 3.0.24
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.
- data/frontends/default/views/_action_group.html.erb +1 -1
- data/frontends/default/views/_action_group.html.erb~ +24 -0
- data/frontends/default/views/_form.html.erb~ +26 -0
- data/frontends/default/views/_form_association.html.erb~ +19 -0
- data/frontends/default/views/_form_association_footer.html.erb~ +16 -6
- data/frontends/default/views/_horizontal_subform.html.erb~ +29 -0
- data/frontends/default/views/_horizontal_subform_header.html.erb~ +3 -2
- data/frontends/default/views/_list_actions.html.erb~ +15 -0
- data/frontends/default/views/_list_inline_adapter.html.erb~ +10 -0
- data/frontends/default/views/_list_messages.html.erb~ +30 -0
- data/frontends/default/views/_list_pagination.html.erb~ +11 -0
- data/frontends/default/views/_list_pagination_links.html.erb~ +0 -0
- data/frontends/default/views/_render_field.js.erb~ +23 -0
- data/frontends/default/views/_row.html.erb~ +6 -0
- data/frontends/default/views/_vertical_subform.html.erb~ +12 -0
- data/frontends/default/views/edit_associated.js.erb~ +13 -0
- data/frontends/default/views/on_create.js.rjs +2 -2
- data/frontends/default/views/render_field.js.erb~ +1 -0
- data/lib/active_scaffold/actions/core.rb~ +13 -5
- data/lib/active_scaffold/actions/create.rb~ +149 -0
- data/lib/active_scaffold/actions/list.rb~ +196 -0
- data/lib/active_scaffold/actions/nested.rb +6 -2
- data/lib/active_scaffold/actions/nested.rb~ +252 -0
- data/lib/active_scaffold/actions/search.rb~ +49 -0
- data/lib/active_scaffold/actions/subform.rb~ +27 -0
- data/lib/active_scaffold/actions/update.rb~ +149 -0
- data/lib/active_scaffold/attribute_params.rb~ +202 -0
- data/lib/active_scaffold/bridges/record_select/{lib/record_select_bridge.rb~ → helpers.rb~} +7 -16
- data/lib/active_scaffold/bridges/shared/date_bridge.rb~ +209 -0
- data/lib/active_scaffold/config/create.rb +4 -4
- data/lib/active_scaffold/config/nested.rb +1 -0
- data/lib/active_scaffold/config/nested.rb~ +41 -0
- data/lib/active_scaffold/config/search.rb~ +74 -0
- data/lib/active_scaffold/constraints.rb~ +186 -0
- data/lib/active_scaffold/data_structures/action_columns.rb~ +140 -0
- data/lib/active_scaffold/data_structures/action_link.rb +4 -4
- data/lib/active_scaffold/data_structures/action_link.rb~ +179 -0
- data/lib/active_scaffold/data_structures/nested_info.rb~ +124 -0
- data/lib/active_scaffold/extensions/action_controller_rendering.rb~ +22 -0
- data/lib/active_scaffold/extensions/action_view_rendering.rb~ +108 -0
- data/lib/active_scaffold/extensions/cache_association.rb~ +12 -0
- data/lib/active_scaffold/extensions/reverse_associations.rb~ +64 -0
- data/lib/active_scaffold/extensions/routing_mapper.rb~ +34 -0
- data/lib/active_scaffold/extensions/unsaved_associated.rb~ +62 -0
- data/lib/active_scaffold/finder.rb~ +370 -0
- data/lib/active_scaffold/helpers/controller_helpers.rb~ +101 -0
- data/lib/active_scaffold/helpers/form_column_helpers.rb~ +321 -0
- data/lib/active_scaffold/helpers/id_helpers.rb~ +123 -0
- data/lib/active_scaffold/helpers/list_column_helpers.rb +9 -6
- data/lib/active_scaffold/helpers/list_column_helpers.rb~ +368 -0
- data/lib/active_scaffold/helpers/search_column_helpers.rb~ +94 -46
- data/lib/active_scaffold/helpers/view_helpers.rb +1 -1
- data/lib/active_scaffold/helpers/view_helpers.rb~ +353 -0
- data/lib/active_scaffold/version.rb +1 -1
- data/lib/active_scaffold.rb +1 -1
- data/lib/active_scaffold.rb~ +362 -0
- metadata +110 -76
- data/lib/active_scaffold/bridges/dragonfly/bridge.rb~ +0 -12
- data/lib/active_scaffold/bridges/dragonfly/lib/dragonfly_bridge.rb~ +0 -36
- data/lib/active_scaffold/bridges/dragonfly/lib/dragonfly_bridge_helpers.rb~ +0 -12
- data/lib/active_scaffold/bridges/dragonfly/lib/form_ui.rb~ +0 -27
- data/lib/active_scaffold/bridges/dragonfly/lib/list_ui.rb~ +0 -16
@@ -0,0 +1,202 @@
|
|
1
|
+
module ActiveScaffold
|
2
|
+
# Provides support for param hashes assumed to be model attributes.
|
3
|
+
# Support is primarily needed for creating/editing associated records using a nested hash structure.
|
4
|
+
#
|
5
|
+
# Paradigm Params Hash (should write unit tests on this):
|
6
|
+
# params[:record] = {
|
7
|
+
# # a simple record attribute
|
8
|
+
# 'name' => 'John',
|
9
|
+
# # a plural association hash
|
10
|
+
# 'roles' => {
|
11
|
+
# # associate with an existing role
|
12
|
+
# '5' => {'id' => 5}
|
13
|
+
# # associate with an existing role and edit it
|
14
|
+
# '6' => {'id' => 6, 'name' => 'designer'}
|
15
|
+
# # create and associate a new role
|
16
|
+
# '124521' => {'name' => 'marketer'}
|
17
|
+
# }
|
18
|
+
# # a singular association hash
|
19
|
+
# 'location' => {'id' => 12, 'city' => 'New York'}
|
20
|
+
# }
|
21
|
+
#
|
22
|
+
# Simpler association structures are also supported, like:
|
23
|
+
# params[:record] = {
|
24
|
+
# # a simple record attribute
|
25
|
+
# 'name' => 'John',
|
26
|
+
# # a plural association ... all ids refer to existing records
|
27
|
+
# 'roles' => ['5', '6'],
|
28
|
+
# # a singular association ... all ids refer to existing records
|
29
|
+
# 'location' => '12'
|
30
|
+
# }
|
31
|
+
module AttributeParams
|
32
|
+
protected
|
33
|
+
# Takes attributes (as from params[:record]) and applies them to the parent_record. Also looks for
|
34
|
+
# association attributes and attempts to instantiate them as associated objects.
|
35
|
+
#
|
36
|
+
# This is a secure way to apply params to a record, because it's based on a loop over the columns
|
37
|
+
# set. The columns set will not yield unauthorized columns, and it will not yield unregistered columns.
|
38
|
+
def update_record_from_params(parent_record, columns, attributes)
|
39
|
+
crud_type = parent_record.new_record? ? :create : :update
|
40
|
+
return parent_record unless parent_record.authorized_for?(:crud_type => crud_type)
|
41
|
+
|
42
|
+
multi_parameter_attributes = {}
|
43
|
+
attributes.each do |k, v|
|
44
|
+
next unless k.include? '('
|
45
|
+
column_name = k.split('(').first.to_sym
|
46
|
+
multi_parameter_attributes[column_name] ||= []
|
47
|
+
multi_parameter_attributes[column_name] << [k, v]
|
48
|
+
end
|
49
|
+
|
50
|
+
columns.each :for => parent_record, :crud_type => crud_type, :flatten => true do |column|
|
51
|
+
# Set any passthrough parameters that may be associated with this column (ie, file column "keep" and "temp" attributes)
|
52
|
+
unless column.params.empty?
|
53
|
+
column.params.each{|p| parent_record.send("#{p}=", attributes[p]) if attributes.has_key? p}
|
54
|
+
end
|
55
|
+
|
56
|
+
if multi_parameter_attributes.has_key? column.name
|
57
|
+
parent_record.send(:assign_multiparameter_attributes, multi_parameter_attributes[column.name])
|
58
|
+
elsif attributes.has_key? column.name
|
59
|
+
value = column_value_from_param_value(parent_record, column, attributes[column.name])
|
60
|
+
|
61
|
+
# we avoid assigning a value that already exists because otherwise has_one associations will break (AR bug in has_one_association.rb#replace)
|
62
|
+
parent_record.send("#{column.name}=", value) unless parent_record.send(column.name) == value
|
63
|
+
|
64
|
+
elsif column.plural_association?
|
65
|
+
parent_record.send("#{column.name}=", [])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if parent_record.new_record?
|
70
|
+
parent_record.class.reflect_on_all_associations.each do |a|
|
71
|
+
next unless [:has_one, :has_many].include?(a.macro) and not (a.options[:through] || a.options[:finder_sql])
|
72
|
+
next unless association_proxy = parent_record.send(a.name)
|
73
|
+
|
74
|
+
raise ActiveScaffold::ReverseAssociationRequired, "Association #{a.name} in class #{parent_record.class.name}: In order to support :has_one and :has_many where the parent record is new and the child record(s) validate the presence of the parent, ActiveScaffold requires the reverse association (the belongs_to)." unless a.reverse
|
75
|
+
|
76
|
+
association_proxy = [association_proxy] if a.macro == :has_one
|
77
|
+
association_proxy.each { |record| record.send("#{a.reverse}=", parent_record) }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
parent_record
|
82
|
+
end
|
83
|
+
|
84
|
+
def manage_nested_record_from_params(parent_record, column, attributes)
|
85
|
+
record = find_or_create_for_params(attributes, column, parent_record)
|
86
|
+
if record
|
87
|
+
record_columns = active_scaffold_config_for(column.association.klass).subform.columns
|
88
|
+
update_record_from_params(record, record_columns, attributes)
|
89
|
+
record.unsaved = true
|
90
|
+
end
|
91
|
+
record
|
92
|
+
end
|
93
|
+
|
94
|
+
def column_value_from_param_value(parent_record, column, value)
|
95
|
+
# convert the value, possibly by instantiating associated objects
|
96
|
+
if value.is_a?(Hash)
|
97
|
+
column_value_from_param_hash_value(parent_record, column, value)
|
98
|
+
else
|
99
|
+
column_value_from_param_simple_value(parent_record, column, value)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def column_value_from_param_simple_value(parent_record, column, value)
|
104
|
+
debugger if column.name == :working_days
|
105
|
+
if column.singular_association?
|
106
|
+
# it's a single id
|
107
|
+
column.association.klass.find(value) if value and not value.empty?
|
108
|
+
elsif column.plural_association?
|
109
|
+
column_plural_assocation_value_from_value(column, value)
|
110
|
+
elsif column.number? && [:i18n_number, :currency].include?(column.options[:format])
|
111
|
+
self.class.i18n_number_to_native_format(value)
|
112
|
+
else
|
113
|
+
# convert empty strings into nil. this works better with 'null => true' columns (and validations),
|
114
|
+
# and 'null => false' columns should just convert back to an empty string.
|
115
|
+
# ... but we can at least check the ConnectionAdapter::Column object to see if nulls are allowed
|
116
|
+
value = nil if value.is_a? String and value.empty? and !column.column.nil? and column.column.null
|
117
|
+
value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def column_plural_assocation_value_from_value(column, value)
|
122
|
+
# it's an array of ids
|
123
|
+
if value and not value.empty?
|
124
|
+
ids = value.select {|id| id.respond_to?(:empty?) ? !id.empty? : true}
|
125
|
+
ids.empty? ? [] : column.association.klass.find(ids)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def column_value_from_param_hash_value(parent_record, column, value)
|
130
|
+
# this is just for backwards compatibility. we should clean this up in 2.0.
|
131
|
+
if column.form_ui == :select
|
132
|
+
ids = if column.singular_association?
|
133
|
+
value[:id]
|
134
|
+
else
|
135
|
+
value.values.collect {|hash| hash[:id]}
|
136
|
+
end
|
137
|
+
(ids and not ids.empty?) ? column.association.klass.find(ids) : nil
|
138
|
+
|
139
|
+
elsif column.singular_association?
|
140
|
+
manage_nested_record_from_params(parent_record, column, value)
|
141
|
+
elsif column.plural_association?
|
142
|
+
value.collect {|key_value_pair| manage_nested_record_from_params(parent_record, column, key_value_pair[1])}.compact
|
143
|
+
else
|
144
|
+
value
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Attempts to create or find an instance of klass (which must be an ActiveRecord object) from the
|
149
|
+
# request parameters given. If params[:id] exists it will attempt to find an existing object
|
150
|
+
# otherwise it will build a new one.
|
151
|
+
def find_or_create_for_params(params, parent_column, parent_record)
|
152
|
+
current = parent_record.send(parent_column.name)
|
153
|
+
klass = parent_column.association.klass
|
154
|
+
pk = klass.primary_key.to_sym
|
155
|
+
return nil if parent_column.show_blank_record?(current) and attributes_hash_is_empty?(params, klass)
|
156
|
+
|
157
|
+
if params.has_key? pk
|
158
|
+
# modifying the current object of a singular association
|
159
|
+
pk_val = params[pk]
|
160
|
+
if current and current.is_a? ActiveRecord::Base and current.id.to_s == pk_val
|
161
|
+
current
|
162
|
+
# modifying one of the current objects in a plural association
|
163
|
+
elsif current and current.respond_to?(:any?) and current.any? {|o| o.id.to_s == pk_val}
|
164
|
+
current.detect {|o| o.id.to_s == pk_val}
|
165
|
+
# attaching an existing but not-current object
|
166
|
+
else
|
167
|
+
klass.find(pk_val)
|
168
|
+
end
|
169
|
+
else
|
170
|
+
build_associated(parent_column, parent_record) if klass.authorized_for?(:crud_type => :create)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
# Determines whether the given attributes hash is "empty".
|
174
|
+
# This isn't a literal emptiness - it's an attempt to discern whether the user intended it to be empty or not.
|
175
|
+
def attributes_hash_is_empty?(hash, klass)
|
176
|
+
ignore_column_types = [:boolean]
|
177
|
+
hash.all? do |key,value|
|
178
|
+
# convert any possible multi-parameter attributes like 'created_at(5i)' to simply 'created_at'
|
179
|
+
parts = key.to_s.split('(')
|
180
|
+
#old style date form management... ignore them too
|
181
|
+
ignore_column_types = [:boolean, :datetime, :date, :time] if parts.length > 1
|
182
|
+
column_name = parts.first
|
183
|
+
column = klass.columns_hash[column_name]
|
184
|
+
|
185
|
+
# booleans and datetimes will always have a value. so we ignore them when checking whether the hash is empty.
|
186
|
+
# this could be a bad idea. but the current situation (excess record entry) seems worse.
|
187
|
+
next true if column and ignore_column_types.include?(column.type)
|
188
|
+
|
189
|
+
# defaults are pre-filled on the form. we can't use them to determine if the user intends a new row.
|
190
|
+
next true if column and value == column.default.to_s
|
191
|
+
|
192
|
+
if value.is_a?(Hash)
|
193
|
+
attributes_hash_is_empty?(value, klass)
|
194
|
+
elsif value.is_a?(Array)
|
195
|
+
value.any? {|id| id.respond_to?(:empty?) ? !id.empty? : true}
|
196
|
+
else
|
197
|
+
value.respond_to?(:empty?) ? value.empty? : false
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -1,20 +1,9 @@
|
|
1
|
-
|
2
|
-
module
|
1
|
+
class ActiveScaffold::Bridges::RecordSelect
|
2
|
+
module Helpers
|
3
3
|
def self.included(base)
|
4
4
|
base.class_eval do
|
5
5
|
include FormColumnHelpers
|
6
6
|
include SearchColumnHelpers
|
7
|
-
include ViewHelpers
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
module ViewHelpers
|
12
|
-
def self.included(base)
|
13
|
-
base.alias_method_chain :active_scaffold_includes, :record_select
|
14
|
-
end
|
15
|
-
|
16
|
-
def active_scaffold_includes_with_record_select(*args)
|
17
|
-
active_scaffold_includes_without_record_select(*args) + record_select_includes
|
18
7
|
end
|
19
8
|
end
|
20
9
|
|
@@ -50,15 +39,17 @@ module ActiveScaffold
|
|
50
39
|
record_select_options.merge!(column.options)
|
51
40
|
if options['data-update_url']
|
52
41
|
record_select_options[:onchange] = %|function(id, label) {
|
53
|
-
ActiveScaffold.update_column(
|
42
|
+
ActiveScaffold.update_column(null, "#{options['data-update_url']}", #{options['data-update_send_form'].to_json}, "#{options[:id]}", id);
|
54
43
|
}|
|
55
44
|
end
|
56
45
|
|
57
|
-
if multiple
|
46
|
+
html = if multiple
|
58
47
|
record_multi_select_field(options[:name], value || [], record_select_options)
|
59
48
|
else
|
60
49
|
record_select_field(options[:name], value || column.association.klass.new, record_select_options)
|
61
50
|
end
|
51
|
+
html = self.class.field_error_proc.call(html, self) if @record.errors[column.name].any?
|
52
|
+
html
|
62
53
|
end
|
63
54
|
end
|
64
55
|
|
@@ -87,4 +78,4 @@ module ActiveScaffold
|
|
87
78
|
end
|
88
79
|
end
|
89
80
|
|
90
|
-
ActionView::Base.class_eval { include ActiveScaffold::
|
81
|
+
ActionView::Base.class_eval { include ActiveScaffold::Bridges::RecordSelect::Helpers }
|
@@ -0,0 +1,209 @@
|
|
1
|
+
module ActiveScaffold
|
2
|
+
module Bridges
|
3
|
+
module Shared
|
4
|
+
module DateBridge
|
5
|
+
module SearchColumnHelpers
|
6
|
+
def active_scaffold_search_date_bridge(column, options)
|
7
|
+
current_search = {'from' => nil, 'to' => nil, 'opt' => 'BETWEEN',
|
8
|
+
'number' => 1, 'unit' => 'DAYS', 'range' => nil}
|
9
|
+
current_search.merge!(options[:value]) unless options[:value].nil?
|
10
|
+
tags = []
|
11
|
+
tags << active_scaffold_search_date_bridge_comparator_tag(column, options, current_search)
|
12
|
+
tags << active_scaffold_search_date_bridge_trend_tag(column, options, current_search)
|
13
|
+
tags << active_scaffold_search_date_bridge_numeric_tag(column, options, current_search)
|
14
|
+
tags << active_scaffold_search_date_bridge_range_tag(column, options, current_search)
|
15
|
+
tags.join(" ").html_safe
|
16
|
+
end
|
17
|
+
|
18
|
+
def active_scaffold_search_date_bridge_comparator_options(column)
|
19
|
+
select_options = ActiveScaffold::Finder::DateComparators.collect {|comp| [as_(comp.downcase.to_sym), comp]}
|
20
|
+
select_options + ActiveScaffold::Finder::NumericComparators.collect {|comp| [as_(comp.downcase.to_sym), comp]}
|
21
|
+
end
|
22
|
+
|
23
|
+
def active_scaffold_search_date_bridge_comparator_tag(column, options, current_search)
|
24
|
+
select_tag("#{options[:name]}[opt]", options_for_select(active_scaffold_search_date_bridge_comparator_options(column),current_search['opt']), :id => "#{options[:id]}_opt", :class => "as_search_range_option as_search_date_time_option")
|
25
|
+
end
|
26
|
+
|
27
|
+
def active_scaffold_search_date_bridge_numeric_tag(column, options, current_search)
|
28
|
+
numeric_controls = "" <<
|
29
|
+
active_scaffold_search_date_bridge_calendar_control(column, options, current_search, 'from') <<
|
30
|
+
content_tag(:span, (" - " + active_scaffold_search_date_bridge_calendar_control(column, options, current_search, 'to')).html_safe,
|
31
|
+
:id => "#{options[:id]}_between", :class => "as_search_range_between", :style => "display:#{current_search['opt'] == 'BETWEEN' ? '' : 'none'}")
|
32
|
+
content_tag("span", numeric_controls.html_safe, :id => "#{options[:id]}_numeric", :style => "display:#{ActiveScaffold::Finder::NumericComparators.include?(current_search['opt']) ? '' : 'none'}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def active_scaffold_search_date_bridge_trend_tag(column, options, current_search)
|
36
|
+
active_scaffold_date_bridge_trend_tag(column, options,
|
37
|
+
{:name_prefix => 'search',
|
38
|
+
:number_value => current_search['number'],
|
39
|
+
:unit_value => current_search["unit"],
|
40
|
+
:show => (current_search['opt'] == 'PAST' || current_search['opt'] == 'FUTURE')})
|
41
|
+
end
|
42
|
+
|
43
|
+
def active_scaffold_date_bridge_trend_tag(column, options, trend_options)
|
44
|
+
trend_controls = text_field_tag("#{trend_options[:name_prefix]}[#{column.name}][number]", trend_options[:number_value], :class => 'text-input', :size => 10, :autocomplete => 'off') << " " <<
|
45
|
+
select_tag("#{trend_options[:name_prefix]}[#{column.name}][unit]",
|
46
|
+
options_for_select(active_scaffold_search_date_bridge_trend_units(column), trend_options[:unit_value]),
|
47
|
+
:class => 'text-input')
|
48
|
+
content_tag("span", trend_controls.html_safe, :id => "#{options[:id]}_trend", :style => "display:#{trend_options[:show] ? '' : 'none'}")
|
49
|
+
end
|
50
|
+
|
51
|
+
def active_scaffold_search_date_bridge_trend_units(column)
|
52
|
+
options = ActiveScaffold::Finder::DateUnits.collect{|unit| [as_(unit.downcase.to_sym), unit]}
|
53
|
+
options = ActiveScaffold::Finder::TimeUnits.collect{|unit| [as_(unit.downcase.to_sym), unit]} + options if column_datetime?(column)
|
54
|
+
options
|
55
|
+
end
|
56
|
+
|
57
|
+
def active_scaffold_search_date_bridge_range_tag(column, options, current_search)
|
58
|
+
range_controls = select_tag("search[#{column.name}][range]",
|
59
|
+
options_for_select( ActiveScaffold::Finder::DateRanges.collect{|range| [as_(range.downcase.to_sym), range]}, current_search["range"]),
|
60
|
+
:class => 'text-input')
|
61
|
+
content_tag("span", range_controls.html_safe, :id => "#{options[:id]}_range", :style => "display:#{(current_search['opt'] == 'RANGE') ? '' : 'none'}")
|
62
|
+
end
|
63
|
+
|
64
|
+
def column_datetime?(column)
|
65
|
+
(!column.column.nil? && [:datetime, :time].include?(column.column.type))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module HumanConditionHelpers
|
70
|
+
def active_scaffold_human_condition_date_bridge(column, value)
|
71
|
+
case value[:opt]
|
72
|
+
when 'RANGE'
|
73
|
+
range_type, range = value[:range].downcase.split('_')
|
74
|
+
format = active_scaffold_human_condition_date_bridge_range_format(range_type, range)
|
75
|
+
from, to = controller.class.date_bridge_from_to(column, value)
|
76
|
+
"#{column.active_record_class.human_attribute_name(column.name)} = #{as_(value[:range].downcase).downcase} (#{I18n.l(from, :format => format)})"
|
77
|
+
when 'PAST', 'FUTURE'
|
78
|
+
from, to = controller.class.date_bridge_from_to(column, value)
|
79
|
+
"#{column.active_record_class.human_attribute_name(column.name)} #{as_('BETWEEN'.downcase).downcase} #{I18n.l(from)} - #{I18n.l(to)}"
|
80
|
+
else
|
81
|
+
from, to = controller.class.date_bridge_from_to(column, value)
|
82
|
+
"#{column.active_record_class.human_attribute_name(column.name)} #{as_(value[:opt].downcase).downcase} #{I18n.l(from)} #{value[:opt] == 'BETWEEN' ? '- ' + I18n.l(to) : ''}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def active_scaffold_human_condition_date_bridge_range_format(range_type, range)
|
87
|
+
case range
|
88
|
+
when 'week'
|
89
|
+
first_day_of_week = I18n.translate 'active_scaffold.date_picker_options.firstDay'
|
90
|
+
if first_day_of_week == 1
|
91
|
+
'%W %Y'
|
92
|
+
else
|
93
|
+
'%U %Y'
|
94
|
+
end
|
95
|
+
when 'month'
|
96
|
+
'%b %Y'
|
97
|
+
when 'year'
|
98
|
+
'%Y'
|
99
|
+
else
|
100
|
+
I18n.translate 'date.formats.default'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
module Finder
|
106
|
+
module ClassMethods
|
107
|
+
def condition_for_date_bridge_type(column, value, like_pattern)
|
108
|
+
operator = ActiveScaffold::Finder::NumericComparators.include?(value[:opt]) && value[:opt] != 'BETWEEN' ? value[:opt] : nil
|
109
|
+
from_value, to_value = date_bridge_from_to(column, value)
|
110
|
+
|
111
|
+
if column.search_sql.is_a? Proc
|
112
|
+
column.search_sql.call(from_value, to_value, operator)
|
113
|
+
else
|
114
|
+
unless operator.nil?
|
115
|
+
["#{column.search_sql} #{value[:opt]} ?", from_value.to_s(:db)] unless from_value.nil?
|
116
|
+
else
|
117
|
+
["#{column.search_sql} BETWEEN ? AND ?", from_value.to_s(:db), to_value.to_s(:db)] unless from_value.nil? && to_value.nil?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def date_bridge_from_to(column, value)
|
123
|
+
conversion = column.column.type == :date ? :to_date : :to_time
|
124
|
+
case value[:opt]
|
125
|
+
when 'RANGE'
|
126
|
+
date_bridge_from_to_for_range(column, value).collect(&conversion)
|
127
|
+
when 'PAST', 'FUTURE'
|
128
|
+
date_bridge_from_to_for_trend(column, value).collect(&conversion)
|
129
|
+
else
|
130
|
+
['from', 'to'].collect { |field| condition_value_for_datetime(value[field], conversion)}
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def date_bridge_now
|
135
|
+
Time.zone.now
|
136
|
+
end
|
137
|
+
|
138
|
+
def date_bridge_from_to_for_trend(column, value)
|
139
|
+
case value['opt']
|
140
|
+
when "PAST"
|
141
|
+
trend_number = [value['number'].to_i, 1].max
|
142
|
+
now = date_bridge_now
|
143
|
+
if date_bridge_column_date?(column)
|
144
|
+
from = now.beginning_of_day.ago((trend_number).send(value['unit'].downcase.singularize.to_sym))
|
145
|
+
to = now.end_of_day
|
146
|
+
else
|
147
|
+
from = now.ago((trend_number).send(value['unit'].downcase.singularize.to_sym))
|
148
|
+
to = now
|
149
|
+
end
|
150
|
+
return from, to
|
151
|
+
when "FUTURE"
|
152
|
+
trend_number = [value['number'].to_i, 1].max
|
153
|
+
now = date_bridge_now
|
154
|
+
if date_bridge_column_date?(column)
|
155
|
+
from = now.beginning_of_day
|
156
|
+
to = now.end_of_day.in((trend_number).send(value['unit'].downcase.singularize.to_sym))
|
157
|
+
else
|
158
|
+
from = now
|
159
|
+
to = now.in((trend_number).send(value['unit'].downcase.singularize.to_sym))
|
160
|
+
end
|
161
|
+
return from, to
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def date_bridge_from_to_for_range(column, value)
|
166
|
+
case value[:range]
|
167
|
+
when 'TODAY'
|
168
|
+
return date_bridge_now.beginning_of_day, date_bridge_now.end_of_day
|
169
|
+
when 'YESTERDAY'
|
170
|
+
return date_bridge_now.ago(1.day).beginning_of_day, date_bridge_now.ago(1.day).end_of_day
|
171
|
+
when 'TOMORROW'
|
172
|
+
return date_bridge_now.in(1.day).beginning_of_day, date_bridge_now.in(1.day).end_of_day
|
173
|
+
else
|
174
|
+
range_type, range = value[:range].downcase.split('_')
|
175
|
+
raise ArgumentError unless ['week', 'month', 'year'].include?(range)
|
176
|
+
case range_type
|
177
|
+
when 'this'
|
178
|
+
return date_bridge_now.send("beginning_of_#{range}".to_sym), date_bridge_now.send("end_of_#{range}")
|
179
|
+
when 'prev'
|
180
|
+
return date_bridge_now.ago(1.send(range.to_sym)).send("beginning_of_#{range}".to_sym), date_bridge_now.ago(1.send(range.to_sym)).send("end_of_#{range}".to_sym)
|
181
|
+
when 'next'
|
182
|
+
return date_bridge_now.in(1.send(range.to_sym)).send("beginning_of_#{range}".to_sym), date_bridge_now.in(1.send(range.to_sym)).send("end_of_#{range}".to_sym)
|
183
|
+
else
|
184
|
+
return nil, nil
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def date_bridge_column_date?(column)
|
190
|
+
if [:date_picker, :datetime_picker].include? column.form_ui
|
191
|
+
column.form_ui == :date_picker
|
192
|
+
else
|
193
|
+
(!column.column.nil? && [:date].include?(column.column.type))
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
ActiveScaffold::Finder.const_set('DateComparators', ["PAST", "FUTURE", "RANGE"])
|
204
|
+
ActiveScaffold::Finder.const_set('DateUnits', ["DAYS", "WEEKS", "MONTHS", "YEARS"])
|
205
|
+
ActiveScaffold::Finder.const_set('TimeUnits', ["SECONDS", "MINUTES", "HOURS"])
|
206
|
+
ActiveScaffold::Finder.const_set('DateRanges', ["TODAY", "YESTERDAY", "TOMORROW",
|
207
|
+
"THIS_WEEK", "PREV_WEEK", "NEXT_WEEK",
|
208
|
+
"THIS_MONTH", "PREV_MONTH", "NEXT_MONTH",
|
209
|
+
"THIS_YEAR", "PREV_YEAR", "NEXT_YEAR"])
|
@@ -4,7 +4,7 @@ module ActiveScaffold::Config
|
|
4
4
|
def initialize(*args)
|
5
5
|
super
|
6
6
|
self.persistent = self.class.persistent
|
7
|
-
self.
|
7
|
+
self.action_after_create = self.class.action_after_create
|
8
8
|
self.refresh_list = self.class.refresh_list
|
9
9
|
end
|
10
10
|
|
@@ -24,8 +24,8 @@ module ActiveScaffold::Config
|
|
24
24
|
@@persistent = false
|
25
25
|
|
26
26
|
# whether update form is opened after a create or not
|
27
|
-
cattr_accessor :
|
28
|
-
@@
|
27
|
+
cattr_accessor :action_after_create
|
28
|
+
@@action_after_create = false
|
29
29
|
|
30
30
|
# whether we should refresh list after create or not
|
31
31
|
cattr_accessor :refresh_list
|
@@ -43,7 +43,7 @@ module ActiveScaffold::Config
|
|
43
43
|
attr_accessor :persistent
|
44
44
|
|
45
45
|
# whether the form stays open after a create or not
|
46
|
-
attr_accessor :
|
46
|
+
attr_accessor :action_after_create
|
47
47
|
|
48
48
|
# whether we should refresh list after create or not
|
49
49
|
attr_accessor :refresh_list
|
@@ -23,6 +23,7 @@ module ActiveScaffold::Config
|
|
23
23
|
unless column.nil? || column.association.nil?
|
24
24
|
options.reverse_merge! :security_method => :nested_authorized?, :label => column.association.klass.model_name.human({:count => 2, :default => column.association.klass.name.pluralize})
|
25
25
|
action_link = @core.link_for_association(column, options)
|
26
|
+
action_link.action ||= :index
|
26
27
|
@core.action_links.add_to_group(action_link, action_group) unless action_link.nil?
|
27
28
|
else
|
28
29
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ActiveScaffold::Config
|
2
|
+
class Nested < Base
|
3
|
+
self.crud_type = :read
|
4
|
+
|
5
|
+
def initialize(core_config)
|
6
|
+
super
|
7
|
+
@label = :add_existing_model
|
8
|
+
self.shallow_delete = self.class.shallow_delete
|
9
|
+
@action_group = self.class.action_group.clone if self.class.action_group
|
10
|
+
end
|
11
|
+
|
12
|
+
# global level configuration
|
13
|
+
# --------------------------
|
14
|
+
cattr_accessor :shallow_delete
|
15
|
+
@@shallow_delete = true
|
16
|
+
|
17
|
+
# instance-level configuration
|
18
|
+
# ----------------------------
|
19
|
+
attr_accessor :shallow_delete
|
20
|
+
|
21
|
+
# Add a nested ActionLink
|
22
|
+
def add_link(attribute, options = {})
|
23
|
+
column = @core.columns[attribute.to_sym]
|
24
|
+
unless column.nil? || column.association.nil?
|
25
|
+
options.reverse_merge! :security_method => :nested_authorized?, :label => column.association.klass.model_name.human({:count => 2, :default => column.association.klass.name.pluralize})
|
26
|
+
action_link = @core.link_for_association(column, options)
|
27
|
+
@core.action_links.add_to_group(action_link, action_group) unless action_link.nil?
|
28
|
+
else
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_scoped_link(named_scope, options = {})
|
34
|
+
action_link = @core.link_for_association_as_scope(named_scope.to_sym, options)
|
35
|
+
@core.action_links.add_to_group(action_link, action_group) unless action_link.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
# the label for this Nested action. used for the header.
|
39
|
+
attr_writer :label
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ActiveScaffold::Config
|
2
|
+
class Search < Base
|
3
|
+
self.crud_type = :read
|
4
|
+
|
5
|
+
def initialize(core_config)
|
6
|
+
super
|
7
|
+
@text_search = self.class.text_search
|
8
|
+
@live = self.class.live?
|
9
|
+
@split_terms = self.class.split_terms
|
10
|
+
|
11
|
+
# start with the ActionLink defined globally
|
12
|
+
@link = self.class.link.clone
|
13
|
+
@action_group = self.class.action_group.clone if self.class.action_group
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# global level configuration
|
18
|
+
# --------------------------
|
19
|
+
# the ActionLink for this action
|
20
|
+
cattr_accessor :link
|
21
|
+
@@link = ActiveScaffold::DataStructures::ActionLink.new('show_search', :label => :search, :type => :collection, :security_method => :search_authorized?, :ignore_method => :search_ignore?)
|
22
|
+
|
23
|
+
# A flag for how the search should do full-text searching in the database:
|
24
|
+
# * :full: LIKE %?%
|
25
|
+
# * :start: LIKE ?%
|
26
|
+
# * :end: LIKE %?
|
27
|
+
# * false: LIKE ?
|
28
|
+
# Default is :full
|
29
|
+
cattr_accessor :text_search
|
30
|
+
@@text_search = :full
|
31
|
+
|
32
|
+
# whether submits the search as you type
|
33
|
+
cattr_writer :live
|
34
|
+
def self.live?
|
35
|
+
@@live
|
36
|
+
end
|
37
|
+
|
38
|
+
# instance-level configuration
|
39
|
+
# ----------------------------
|
40
|
+
|
41
|
+
# provides access to the list of columns specifically meant for the Search to use
|
42
|
+
def columns
|
43
|
+
# we want to delay initializing to the @core.columns set for as long as possible. Too soon and .search_sql will not be available to .searchable?
|
44
|
+
unless @columns
|
45
|
+
debugger
|
46
|
+
self.columns = @core.columns.collect{|c| c.name if @core.columns._inheritable.include?(c.name) and c.searchable? and c.column and c.column.text?}.compact
|
47
|
+
end
|
48
|
+
@columns
|
49
|
+
end
|
50
|
+
|
51
|
+
public :columns=
|
52
|
+
|
53
|
+
# A flag for how the search should do full-text searching in the database:
|
54
|
+
# * :full: LIKE %?%
|
55
|
+
# * :start: LIKE ?%
|
56
|
+
# * :end: LIKE %?
|
57
|
+
# * false: LIKE ?
|
58
|
+
# Default is :full
|
59
|
+
attr_accessor :text_search
|
60
|
+
|
61
|
+
@@split_terms = " "
|
62
|
+
cattr_accessor :split_terms
|
63
|
+
attr_accessor :split_terms
|
64
|
+
|
65
|
+
# the ActionLink for this action
|
66
|
+
attr_accessor :link
|
67
|
+
|
68
|
+
# whether submits the search as you type
|
69
|
+
attr_writer :live
|
70
|
+
def live?
|
71
|
+
@live
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|