active_scaffold 3.0.23 → 3.0.24

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/frontends/default/views/_action_group.html.erb +1 -1
  2. data/frontends/default/views/_action_group.html.erb~ +24 -0
  3. data/frontends/default/views/_form.html.erb~ +26 -0
  4. data/frontends/default/views/_form_association.html.erb~ +19 -0
  5. data/frontends/default/views/_form_association_footer.html.erb~ +16 -6
  6. data/frontends/default/views/_horizontal_subform.html.erb~ +29 -0
  7. data/frontends/default/views/_horizontal_subform_header.html.erb~ +3 -2
  8. data/frontends/default/views/_list_actions.html.erb~ +15 -0
  9. data/frontends/default/views/_list_inline_adapter.html.erb~ +10 -0
  10. data/frontends/default/views/_list_messages.html.erb~ +30 -0
  11. data/frontends/default/views/_list_pagination.html.erb~ +11 -0
  12. data/frontends/default/views/_list_pagination_links.html.erb~ +0 -0
  13. data/frontends/default/views/_render_field.js.erb~ +23 -0
  14. data/frontends/default/views/_row.html.erb~ +6 -0
  15. data/frontends/default/views/_vertical_subform.html.erb~ +12 -0
  16. data/frontends/default/views/edit_associated.js.erb~ +13 -0
  17. data/frontends/default/views/on_create.js.rjs +2 -2
  18. data/frontends/default/views/render_field.js.erb~ +1 -0
  19. data/lib/active_scaffold/actions/core.rb~ +13 -5
  20. data/lib/active_scaffold/actions/create.rb~ +149 -0
  21. data/lib/active_scaffold/actions/list.rb~ +196 -0
  22. data/lib/active_scaffold/actions/nested.rb +6 -2
  23. data/lib/active_scaffold/actions/nested.rb~ +252 -0
  24. data/lib/active_scaffold/actions/search.rb~ +49 -0
  25. data/lib/active_scaffold/actions/subform.rb~ +27 -0
  26. data/lib/active_scaffold/actions/update.rb~ +149 -0
  27. data/lib/active_scaffold/attribute_params.rb~ +202 -0
  28. data/lib/active_scaffold/bridges/record_select/{lib/record_select_bridge.rb~ → helpers.rb~} +7 -16
  29. data/lib/active_scaffold/bridges/shared/date_bridge.rb~ +209 -0
  30. data/lib/active_scaffold/config/create.rb +4 -4
  31. data/lib/active_scaffold/config/nested.rb +1 -0
  32. data/lib/active_scaffold/config/nested.rb~ +41 -0
  33. data/lib/active_scaffold/config/search.rb~ +74 -0
  34. data/lib/active_scaffold/constraints.rb~ +186 -0
  35. data/lib/active_scaffold/data_structures/action_columns.rb~ +140 -0
  36. data/lib/active_scaffold/data_structures/action_link.rb +4 -4
  37. data/lib/active_scaffold/data_structures/action_link.rb~ +179 -0
  38. data/lib/active_scaffold/data_structures/nested_info.rb~ +124 -0
  39. data/lib/active_scaffold/extensions/action_controller_rendering.rb~ +22 -0
  40. data/lib/active_scaffold/extensions/action_view_rendering.rb~ +108 -0
  41. data/lib/active_scaffold/extensions/cache_association.rb~ +12 -0
  42. data/lib/active_scaffold/extensions/reverse_associations.rb~ +64 -0
  43. data/lib/active_scaffold/extensions/routing_mapper.rb~ +34 -0
  44. data/lib/active_scaffold/extensions/unsaved_associated.rb~ +62 -0
  45. data/lib/active_scaffold/finder.rb~ +370 -0
  46. data/lib/active_scaffold/helpers/controller_helpers.rb~ +101 -0
  47. data/lib/active_scaffold/helpers/form_column_helpers.rb~ +321 -0
  48. data/lib/active_scaffold/helpers/id_helpers.rb~ +123 -0
  49. data/lib/active_scaffold/helpers/list_column_helpers.rb +9 -6
  50. data/lib/active_scaffold/helpers/list_column_helpers.rb~ +368 -0
  51. data/lib/active_scaffold/helpers/search_column_helpers.rb~ +94 -46
  52. data/lib/active_scaffold/helpers/view_helpers.rb +1 -1
  53. data/lib/active_scaffold/helpers/view_helpers.rb~ +353 -0
  54. data/lib/active_scaffold/version.rb +1 -1
  55. data/lib/active_scaffold.rb +1 -1
  56. data/lib/active_scaffold.rb~ +362 -0
  57. metadata +110 -76
  58. data/lib/active_scaffold/bridges/dragonfly/bridge.rb~ +0 -12
  59. data/lib/active_scaffold/bridges/dragonfly/lib/dragonfly_bridge.rb~ +0 -36
  60. data/lib/active_scaffold/bridges/dragonfly/lib/dragonfly_bridge_helpers.rb~ +0 -12
  61. data/lib/active_scaffold/bridges/dragonfly/lib/form_ui.rb~ +0 -27
  62. 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
- module ActiveScaffold
2
- module RecordSelectBridge
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($("##{options[:id]}"), "#{options['data-update_url']}", #{options['data-update_send_form'].to_json}, "#{options[:id]}", id);
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::RecordSelectBridge }
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("&nbsp;").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.edit_after_create = self.class.edit_after_create
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 :edit_after_create
28
- @@edit_after_create = false
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 :edit_after_create
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