active_scaffold 3.3.3 → 3.4.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.
Files changed (198) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +39 -0
  3. data/README.md +5 -3
  4. data/app/assets/images/active_scaffold/refresh.png +0 -0
  5. data/app/assets/javascripts/jquery/active_scaffold.js +182 -91
  6. data/app/assets/javascripts/jquery/date_picker_bridge.js.erb +14 -16
  7. data/app/assets/javascripts/jquery/draggable_lists.js +33 -26
  8. data/app/assets/javascripts/jquery/jquery.editinplace.js +3 -3
  9. data/app/assets/javascripts/prototype/active_scaffold.js +61 -19
  10. data/app/assets/stylesheets/active_scaffold_colors.css.scss +4 -0
  11. data/app/assets/stylesheets/active_scaffold_images.css.scss +3 -0
  12. data/app/assets/stylesheets/active_scaffold_layout.css +23 -2
  13. data/app/views/active_scaffold_overrides/_add_existing_form.html.erb +1 -3
  14. data/app/views/active_scaffold_overrides/_base_form.html.erb +7 -5
  15. data/app/views/active_scaffold_overrides/_field_search.html.erb +1 -2
  16. data/app/views/active_scaffold_overrides/_form.html.erb +6 -4
  17. data/app/views/active_scaffold_overrides/_form_association.html.erb +4 -3
  18. data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +5 -5
  19. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +8 -6
  20. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +3 -2
  21. data/app/views/active_scaffold_overrides/_list.html.erb +8 -6
  22. data/app/views/active_scaffold_overrides/_list_column_headings.html.erb +1 -4
  23. data/app/views/active_scaffold_overrides/_list_pagination.html.erb +4 -4
  24. data/app/views/active_scaffold_overrides/_list_pagination_links.html.erb +1 -1
  25. data/app/views/active_scaffold_overrides/_list_record.html.erb +3 -3
  26. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +8 -1
  27. data/app/views/active_scaffold_overrides/_search.html.erb +7 -13
  28. data/app/views/active_scaffold_overrides/_show_columns.html.erb +1 -1
  29. data/app/views/active_scaffold_overrides/on_create.js.erb +4 -4
  30. data/app/views/active_scaffold_overrides/render_field_inplace.html.erb +1 -1
  31. data/app/views/active_scaffold_overrides/row.js.erb +1 -1
  32. data/config/locales/de.yml +106 -95
  33. data/config/locales/en.yml +108 -97
  34. data/config/locales/es.yml +109 -98
  35. data/config/locales/fr.yml +108 -97
  36. data/config/locales/hu.yml +109 -98
  37. data/config/locales/ja.yml +100 -89
  38. data/config/locales/ru.yml +115 -104
  39. data/lib/active_scaffold.rb +18 -294
  40. data/lib/active_scaffold/actions/common_search.rb +50 -17
  41. data/lib/active_scaffold/actions/core.rb +93 -22
  42. data/lib/active_scaffold/actions/create.rb +15 -6
  43. data/lib/active_scaffold/actions/field_search.rb +68 -60
  44. data/lib/active_scaffold/actions/list.rb +49 -28
  45. data/lib/active_scaffold/actions/nested.rb +14 -6
  46. data/lib/active_scaffold/actions/search.rb +36 -35
  47. data/lib/active_scaffold/actions/show.rb +9 -4
  48. data/lib/active_scaffold/actions/subform.rb +1 -1
  49. data/lib/active_scaffold/actions/update.rb +22 -7
  50. data/lib/active_scaffold/active_record_permissions.rb +125 -118
  51. data/lib/active_scaffold/attribute_params.rb +84 -66
  52. data/lib/active_scaffold/bridges.rb +3 -3
  53. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +10 -5
  54. data/lib/active_scaffold/bridges/cancan.rb +2 -1
  55. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +13 -2
  56. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +11 -6
  57. data/lib/active_scaffold/bridges/chosen/helpers.rb +2 -2
  58. data/lib/active_scaffold/bridges/country_helper/country_helper_bridge.rb +45 -29
  59. data/lib/active_scaffold/bridges/date_picker/ext.rb +11 -6
  60. data/lib/active_scaffold/bridges/date_picker/helper.rb +5 -1
  61. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +10 -5
  62. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +6 -1
  63. data/lib/active_scaffold/bridges/file_column/form_ui.rb +12 -11
  64. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +14 -6
  65. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  66. data/lib/active_scaffold/bridges/record_select/helpers.rb +15 -12
  67. data/lib/active_scaffold/bridges/shared/date_bridge.rb +7 -8
  68. data/lib/active_scaffold/bridges/tiny_mce.rb +5 -3
  69. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +4 -5
  70. data/lib/active_scaffold/config/base.rb +4 -0
  71. data/lib/active_scaffold/config/core.rb +12 -5
  72. data/lib/active_scaffold/config/delete.rb +0 -2
  73. data/lib/active_scaffold/config/field_search.rb +1 -4
  74. data/lib/active_scaffold/config/form.rb +0 -2
  75. data/lib/active_scaffold/config/list.rb +31 -1
  76. data/lib/active_scaffold/config/search.rb +0 -3
  77. data/lib/active_scaffold/config/show.rb +0 -6
  78. data/lib/active_scaffold/config/subform.rb +1 -0
  79. data/lib/active_scaffold/configurable.rb +2 -2
  80. data/lib/active_scaffold/constraints.rb +11 -14
  81. data/lib/active_scaffold/core.rb +277 -0
  82. data/lib/active_scaffold/data_structures/action_columns.rb +18 -2
  83. data/lib/active_scaffold/data_structures/action_link.rb +25 -6
  84. data/lib/active_scaffold/data_structures/action_links.rb +9 -4
  85. data/lib/active_scaffold/data_structures/actions.rb +1 -1
  86. data/lib/active_scaffold/data_structures/column.rb +6 -6
  87. data/lib/active_scaffold/data_structures/columns.rb +2 -2
  88. data/lib/active_scaffold/data_structures/nested_info.rb +5 -1
  89. data/lib/active_scaffold/data_structures/sorting.rb +15 -5
  90. data/lib/active_scaffold/delayed_setup.rb +30 -0
  91. data/lib/active_scaffold/engine.rb +25 -0
  92. data/lib/active_scaffold/extensions/action_view_rendering.rb +1 -1
  93. data/lib/active_scaffold/extensions/left_outer_joins.rb +61 -21
  94. data/lib/active_scaffold/extensions/localize.rb +1 -1
  95. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +13 -8
  96. data/lib/active_scaffold/extensions/paginator_extensions.rb +5 -1
  97. data/lib/active_scaffold/extensions/reverse_associations.rb +1 -0
  98. data/lib/active_scaffold/extensions/routing_mapper.rb +1 -1
  99. data/lib/active_scaffold/extensions/unsaved_record.rb +4 -6
  100. data/lib/active_scaffold/finder.rb +79 -27
  101. data/lib/active_scaffold/helpers/association_helpers.rb +48 -18
  102. data/lib/active_scaffold/helpers/controller_helpers.rb +19 -10
  103. data/lib/active_scaffold/helpers/form_column_helpers.rb +185 -87
  104. data/lib/active_scaffold/helpers/human_condition_helpers.rb +2 -1
  105. data/lib/active_scaffold/helpers/id_helpers.rb +14 -8
  106. data/lib/active_scaffold/helpers/list_column_helpers.rb +65 -56
  107. data/lib/active_scaffold/helpers/pagination_helpers.rb +5 -1
  108. data/lib/active_scaffold/helpers/search_column_helpers.rb +21 -18
  109. data/lib/active_scaffold/helpers/view_helpers.rb +102 -64
  110. data/lib/active_scaffold/responds_to_parent.rb +39 -64
  111. data/lib/active_scaffold/tableless.rb +129 -10
  112. data/lib/active_scaffold/version.rb +2 -2
  113. data/test/bridges/bridge_test.rb +1 -1
  114. data/test/bridges/date_picker_test.rb +2 -2
  115. data/test/bridges/paperclip_test.rb +10 -8
  116. data/test/bridges/tiny_mce_test.rb +2 -2
  117. data/test/company.rb +22 -10
  118. data/test/config/base_test.rb +1 -1
  119. data/test/config/core_test.rb +8 -6
  120. data/test/config/create_test.rb +6 -6
  121. data/test/config/delete_test.rb +4 -4
  122. data/test/config/field_search_test.rb +6 -6
  123. data/test/config/list_test.rb +7 -7
  124. data/test/config/nested_test.rb +8 -7
  125. data/test/config/search_test.rb +7 -7
  126. data/test/config/show_test.rb +5 -5
  127. data/test/config/subform_test.rb +1 -1
  128. data/test/config/update_test.rb +5 -4
  129. data/test/data_structures/action_columns_test.rb +15 -16
  130. data/test/data_structures/action_link_test.rb +10 -10
  131. data/test/data_structures/action_links_test.rb +6 -6
  132. data/test/data_structures/actions_test.rb +4 -4
  133. data/test/data_structures/association_column_test.rb +4 -4
  134. data/test/data_structures/column_test.rb +9 -9
  135. data/test/data_structures/columns_test.rb +7 -7
  136. data/test/data_structures/error_message_test.rb +2 -4
  137. data/test/data_structures/set_test.rb +13 -13
  138. data/test/data_structures/sorting_test.rb +8 -8
  139. data/test/data_structures/standard_column_test.rb +2 -2
  140. data/test/data_structures/validation_reflection_test.rb +8 -8
  141. data/test/data_structures/virtual_column_test.rb +5 -5
  142. data/test/extensions/active_record_test.rb +1 -1
  143. data/test/helpers/form_column_helpers_test.rb +5 -5
  144. data/test/helpers/list_column_helpers_test.rb +2 -1
  145. data/test/helpers/pagination_helpers_test.rb +1 -1
  146. data/test/misc/active_record_permissions_test.rb +23 -4
  147. data/test/misc/attribute_params_test.rb +304 -136
  148. data/test/misc/calculation_test.rb +55 -0
  149. data/test/misc/configurable_test.rb +22 -21
  150. data/test/misc/constraints_test.rb +10 -7
  151. data/test/misc/convert_numbers_format_test.rb +149 -0
  152. data/test/misc/finder_test.rb +17 -13
  153. data/test/misc/lang_test.rb +1 -1
  154. data/test/misc/tableless_test.rb +18 -0
  155. data/test/mock_app/app/controllers/addresses_controller.rb +4 -0
  156. data/test/mock_app/app/controllers/buildings_controller.rb +4 -0
  157. data/test/mock_app/app/controllers/cars_controller.rb +4 -0
  158. data/test/mock_app/app/controllers/contacts_controller.rb +4 -0
  159. data/test/mock_app/app/controllers/floors_controller.rb +6 -0
  160. data/test/mock_app/app/controllers/people_controller.rb +4 -0
  161. data/test/mock_app/app/models/address.rb +3 -0
  162. data/test/mock_app/app/models/building.rb +8 -0
  163. data/test/mock_app/app/models/car.rb +3 -0
  164. data/test/mock_app/app/models/contact.rb +3 -0
  165. data/test/mock_app/app/models/file_model.rb +19 -0
  166. data/test/mock_app/app/models/floor.rb +8 -0
  167. data/test/mock_app/app/models/person.rb +11 -0
  168. data/test/mock_app/config/application.rb +2 -0
  169. data/test/mock_app/config/environments/test.rb +1 -1
  170. data/test/mock_app/config/initializers/secret_token.rb +5 -1
  171. data/test/mock_app/config/routes.rb +1 -1
  172. data/test/mock_app/db/schema.rb +51 -0
  173. data/test/model_stub.rb +3 -3
  174. data/test/test_helper.rb +15 -12
  175. metadata +51 -50
  176. data/lib/active_scaffold/extensions/array.rb +0 -7
  177. data/lib/active_scaffold/extensions/cache_association.rb +0 -16
  178. data/lib/active_scaffold/extensions/usa_state.rb +0 -46
  179. data/lib/active_scaffold_env.rb +0 -13
  180. data/test/extensions/array_test.rb +0 -12
  181. data/test/mock_app/public/blank.html +0 -33
  182. data/test/mock_app/public/images/active_scaffold/DO_NOT_EDIT +0 -2
  183. data/test/mock_app/public/images/active_scaffold/default/add.gif +0 -0
  184. data/test/mock_app/public/images/active_scaffold/default/arrow_down.gif +0 -0
  185. data/test/mock_app/public/images/active_scaffold/default/arrow_up.gif +0 -0
  186. data/test/mock_app/public/images/active_scaffold/default/close.gif +0 -0
  187. data/test/mock_app/public/images/active_scaffold/default/cross.png +0 -0
  188. data/test/mock_app/public/images/active_scaffold/default/indicator-small.gif +0 -0
  189. data/test/mock_app/public/images/active_scaffold/default/indicator.gif +0 -0
  190. data/test/mock_app/public/images/active_scaffold/default/magnifier.png +0 -0
  191. data/test/mock_app/public/javascripts/active_scaffold/DO_NOT_EDIT +0 -2
  192. data/test/mock_app/public/javascripts/active_scaffold/default/active_scaffold.js +0 -532
  193. data/test/mock_app/public/javascripts/active_scaffold/default/dhtml_history.js +0 -867
  194. data/test/mock_app/public/javascripts/active_scaffold/default/form_enhancements.js +0 -117
  195. data/test/mock_app/public/javascripts/active_scaffold/default/rico_corner.js +0 -370
  196. data/test/mock_app/public/stylesheets/active_scaffold/DO_NOT_EDIT +0 -2
  197. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet-ie.css +0 -35
  198. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet.css +0 -848
@@ -10,7 +10,7 @@ module ActiveScaffold
10
10
  value = ' '.html_safe if value.nil? or value.blank? # fix for IE 6
11
11
  return value
12
12
  rescue Exception => e
13
- logger.error "#{Time.now.to_s} #{e.inspect} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
13
+ logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}, record: #{record.inspect}"
14
14
  raise e
15
15
  end
16
16
  end
@@ -41,16 +41,21 @@ module ActiveScaffold
41
41
  # TODO: move empty_field_text and   logic in here?
42
42
  # TODO: we need to distinguish between the automatic links *we* create and the ones that the dev specified. some logic may not apply if the dev specified the link.
43
43
  def render_list_column(text, column, record)
44
- if column.link && !skip_action_link?(column.link, record)
45
- link = column.link
46
- associated = record.send(column.association.name) if column.association
47
- render_action_link(link, record, :link => text, :authorized => link.action.nil? || column_link_authorized?(link, column, record, associated))
48
- elsif inplace_edit?(record, column)
49
- active_scaffold_inplace_edit(record, column, {:formatted_column => text})
50
- elsif active_scaffold_config.list.wrap_tag
51
- content_tag active_scaffold_config.list.wrap_tag, text
52
- else
53
- text
44
+ begin
45
+ if column.link && !skip_action_link?(column.link, record)
46
+ link = column.link
47
+ associated = record.send(column.association.name) if column.association
48
+ render_action_link(link, record, :link => text, :authorized => link.action.nil? || column_link_authorized?(link, column, record, associated))
49
+ elsif inplace_edit?(record, column)
50
+ active_scaffold_inplace_edit(record, column, {:formatted_column => text})
51
+ elsif active_scaffold_config.list.wrap_tag
52
+ content_tag active_scaffold_config.list.wrap_tag, text
53
+ else
54
+ text
55
+ end
56
+ rescue Exception => e
57
+ logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
58
+ raise e
54
59
  end
55
60
  end
56
61
 
@@ -78,7 +83,7 @@ module ActiveScaffold
78
83
 
79
84
  def active_scaffold_column_marked(record, column)
80
85
  options = {:id => nil, :object => record}
81
- content_tag(:span, check_box(:record, column.name, options), :class => 'in_place_editor_field', :data => {:ie_id => record.id.to_s})
86
+ content_tag(:span, check_box(:record, column.name, options), :class => 'in_place_editor_field', :data => {:ie_id => record.to_param})
82
87
  end
83
88
 
84
89
  def active_scaffold_column_checkbox(record, column)
@@ -106,11 +111,7 @@ module ActiveScaffold
106
111
  ##
107
112
  def format_column_value(record, column, value = nil)
108
113
  value ||= record.send(column.name) unless record.nil?
109
- if value && column.association # cache association size before calling column_empty?
110
- associated_size = value.size if column.plural_association? and column.associated_number? # get count before cache association
111
- cache_association(value, column, associated_size) if column.plural_association?
112
- end
113
- if column.association.nil? or column_empty?(value)
114
+ if column.association.nil?
114
115
  if column.form_ui == :select && column.options[:options]
115
116
  text, val = column.options[:options].find {|text, val| (val.nil? ? text : val).to_s == value.to_s}
116
117
  value = active_scaffold_translated_option(column, text, val).first if text
@@ -121,6 +122,10 @@ module ActiveScaffold
121
122
  format_value(value, column.options)
122
123
  end
123
124
  else
125
+ if column.plural_association?
126
+ associated_size = value.size if column.associated_number? # get count before cache association
127
+ cache_association(record.association(column.name), column, associated_size) unless value.loaded?
128
+ end
124
129
  format_association_value(value, column, associated_size)
125
130
  end
126
131
  end
@@ -142,29 +147,30 @@ module ActiveScaffold
142
147
  end
143
148
 
144
149
  def format_association_value(value, column, size)
145
- format_value case column.association.macro
146
- when :has_one, :belongs_to
147
- if column.polymorphic_association?
148
- "#{value.class.model_name.human}: #{value.to_label}"
149
- else
150
- value.to_label
151
- end
152
- when :has_many, :has_and_belongs_to_many
153
- if column.associated_limit.nil?
154
- firsts = value.collect { |v| v.to_label }
155
- else
156
- firsts = value.first(column.associated_limit)
157
- firsts.collect! { |v| v.to_label }
158
- firsts[column.associated_limit] = '…' if value.size > column.associated_limit
159
- end
160
- if column.associated_limit == 0
161
- size if column.associated_number?
162
- else
163
- joined_associated = firsts.join(h(active_scaffold_config.list.association_join_text)).html_safe
164
- joined_associated << " (#{size})" if column.associated_number? and column.associated_limit and value.size > column.associated_limit
165
- joined_associated
166
- end
150
+ method = column.options[:label_method] || :to_label
151
+ value = if column.association.collection?
152
+ if column.associated_limit.nil?
153
+ firsts = value.collect(&method)
154
+ else
155
+ firsts = value.first(column.associated_limit)
156
+ firsts.collect!(&method)
157
+ firsts[column.associated_limit] = '…' if value.size > column.associated_limit
158
+ end
159
+ if column.associated_limit == 0
160
+ size if column.associated_number?
161
+ else
162
+ joined_associated = firsts.join(h(active_scaffold_config.list.association_join_text)).html_safe
163
+ joined_associated << " (#{size})" if column.associated_number? and column.associated_limit and value.size > column.associated_limit
164
+ joined_associated
165
+ end
166
+ elsif value
167
+ if column.polymorphic_association?
168
+ "#{value.class.model_name.human}: #{value.send(method)}"
169
+ else
170
+ value.send(method)
171
+ end
167
172
  end
173
+ format_value value
168
174
  end
169
175
 
170
176
  def format_value(column_value, options = {})
@@ -180,17 +186,15 @@ module ActiveScaffold
180
186
  clean_column_value(value)
181
187
  end
182
188
 
183
- def cache_association(value, column, size)
184
- # we are not using eager loading, cache firsts records in order not to query the database in a future
185
- unless value.loaded?
186
- # load at least one record, is needed to display '...'
187
- if column.associated_limit.nil?
188
- Rails.logger.warn "ActiveScaffold: Enable eager loading for #{column.name} association to reduce SQL queries"
189
- elsif column.associated_limit > 0
190
- value.target = value.find(:all, :limit => column.associated_limit + 1, :select => column.select_associated_columns)
191
- elsif @cache_associations
192
- value.target = size.to_i.zero? ? [] : [nil]
193
- end
189
+ def cache_association(association, column, size)
190
+ # we are not using eager loading, cache firsts records in order not to query the database for whole association in a future
191
+ if column.associated_limit.nil?
192
+ logger.warn "ActiveScaffold: Enable eager loading for #{column.name} association to reduce SQL queries"
193
+ elsif column.associated_limit > 0
194
+ # load at least one record more, is needed to display '...'
195
+ association.target = association.reader.limit(column.associated_limit + 1).select(column.select_associated_columns || '*').to_a
196
+ elsif @cache_associations
197
+ association.target = []
194
198
  end
195
199
  end
196
200
 
@@ -209,15 +213,18 @@ module ActiveScaffold
209
213
  column.inplace_edit != :ajax and (override_form_field?(column) or column.form_ui or (column.column and override_input?(column.column.type)))
210
214
  end
211
215
 
212
- def active_scaffold_inplace_edit(record, column, options = {})
213
- formatted_column = options[:formatted_column] || format_column_value(record, column)
216
+ def active_scaffold_inplace_edit_tag_options(record, column)
214
217
  id_options = {:id => record.id.to_s, :action => 'update_column', :name => column.name.to_s}
215
218
  tag_options = {:id => element_cell_id(id_options), :class => "in_place_editor_field",
216
- :title => as_(:click_to_edit), :data => {:ie_id => record.id.to_s}}
219
+ :title => as_(:click_to_edit), :data => {:ie_id => record.to_param}}
217
220
  tag_options[:data][:ie_update] = column.inplace_edit if column.inplace_edit != true
221
+ tag_options
222
+ end
218
223
 
224
+ def active_scaffold_inplace_edit(record, column, options = {})
225
+ formatted_column = options[:formatted_column] || format_column_value(record, column)
219
226
  content_tag(:span, as_(:inplace_edit_handle), :class => 'handle') <<
220
- content_tag(:span, formatted_column, tag_options)
227
+ content_tag(:span, formatted_column, active_scaffold_inplace_edit_tag_options(record, column))
221
228
  end
222
229
 
223
230
  def inplace_edit_control(column)
@@ -228,8 +235,10 @@ module ActiveScaffold
228
235
  column.form_ui = :select if (column.association && column.form_ui.nil?)
229
236
  options = active_scaffold_input_options(column).merge(:object => new_model)
230
237
  options[:class] = "#{options[:class]} inplace_field"
238
+ options[:"data-id"] = options[:id]
239
+ options[:id] = nil
231
240
  content_tag(:div, active_scaffold_input_for(column, nil, options), :style => "display:none;", :class => inplace_edit_control_css_class).tap do
232
- @record = old_record
241
+ @record = old_record # TODO remove when relying on @record is removed
233
242
  end
234
243
  end
235
244
  end
@@ -280,7 +289,7 @@ module ActiveScaffold
280
289
  end
281
290
 
282
291
  def column_heading_attributes(column, sorting, sort_direction)
283
- tag_options = {:id => active_scaffold_column_header_id(column), :class => column_heading_class(column, sorting), :title => strip_tags(column.description)}
292
+ tag_options = {:id => active_scaffold_column_header_id(column), :class => column_heading_class(column, sorting), :title => strip_tags(column.description).presence}
284
293
  end
285
294
 
286
295
  def render_column_heading(column, sorting, sort_direction)
@@ -5,7 +5,8 @@ module ActiveScaffold
5
5
  link_to page_number, url_options.merge(:page => page_number), options.merge(:class => "as_paginate")
6
6
  end
7
7
 
8
- def pagination_ajax_links(current_page, url_options, options, inner_window, outer_window)
8
+ def pagination_url_options(url_options = nil)
9
+ url_options ||= params_for(:action => :index)
9
10
  unless active_scaffold_config.store_user_settings
10
11
  url_options.merge!(:search => search_params) if search_params.present?
11
12
  if active_scaffold_config.list.user.user_sorting?
@@ -13,7 +14,10 @@ module ActiveScaffold
13
14
  url_options.merge!(:sort => column.name, :sort_direction => direction)
14
15
  end
15
16
  end
17
+ url_options
18
+ end
16
19
 
20
+ def pagination_ajax_links(current_page, url_options, options, inner_window, outer_window)
17
21
  start_number = current_page.number - inner_window
18
22
  end_number = current_page.number + inner_window
19
23
  start_number = 1 if start_number <= 0
@@ -6,10 +6,13 @@ module ActiveScaffold
6
6
  # It does not do any rendering. It only decides which method is responsible for rendering.
7
7
  def active_scaffold_search_for(column, options = nil)
8
8
  options ||= active_scaffold_search_options(column)
9
+ record = options[:object]
10
+ ActiveSupport::Deprecation.warn "Relying on @record is deprecated, include :object in options with record.", caller if record.nil? # TODO Remove when relying on @record is removed
11
+ record ||= @record # TODO Remove when relying on @record is removed
9
12
 
10
13
  # first, check if the dev has created an override for this specific field for search
11
14
  if (method = override_search_field(column))
12
- send(method, options[:object] || @record, options)
15
+ send(method, record, options)
13
16
 
14
17
  # second, check if the dev has specified a valid search_ui for this column, using specific ui for searches
15
18
  elsif column.search_ui and (method = override_search(column.search_ui))
@@ -21,7 +24,7 @@ module ActiveScaffold
21
24
 
22
25
  # fourth, check if the dev has created an override for this specific field
23
26
  elsif (method = override_form_field(column))
24
- send(method, options[:object] || @record, options)
27
+ send(method, record, options)
25
28
 
26
29
  # fallback: we get to make the decision
27
30
  else
@@ -44,6 +47,9 @@ module ActiveScaffold
44
47
  end
45
48
  end
46
49
  end
50
+ rescue Exception => e
51
+ logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
52
+ raise e
47
53
  end
48
54
 
49
55
  # the standard active scaffold options used for class, name and scope
@@ -66,12 +72,16 @@ module ActiveScaffold
66
72
  ##
67
73
 
68
74
  def active_scaffold_search_multi_select(column, options)
75
+ record = options.delete(:object)
76
+ ActiveSupport::Deprecation.warn "Relying on @record is deprecated, include :object in options with record.", caller if record.nil? # TODO Remove when relying on @record is removed
77
+ record ||= @record # TODO Remove when relying on @record is removed
69
78
  associated = options.delete :value
70
79
  associated = [associated].compact unless associated.is_a? Array
71
80
  associated.collect!(&:to_i)
72
81
 
73
82
  if column.association
74
- select_options = sorted_association_options_find(column.association).collect {|r| [r.to_label, r.id]}
83
+ method = column.options[:label_method] || :to_label
84
+ select_options = sorted_association_options_find(column.association, nil, record).collect {|r| [r.send(method), r.id]}
75
85
  else
76
86
  select_options = column.options[:options].collect do |text, value|
77
87
  active_scaffold_translated_option(column, text, value)
@@ -83,11 +93,14 @@ module ActiveScaffold
83
93
  end
84
94
 
85
95
  def active_scaffold_search_select(column, html_options, options = {})
96
+ record = html_options.delete(:object)
97
+ ActiveSupport::Deprecation.warn "Relying on @record is deprecated, include :object in html_options with record.", caller if record.nil? # TODO Remove when relying on @record is removed
98
+ record ||= @record # TODO Remove when relying on @record is removed
86
99
  associated = html_options.delete :value
87
100
  if column.association
88
101
  associated = associated.is_a?(Array) ? associated.map(&:to_i) : associated.to_i unless associated.nil?
89
102
  method = column.association.macro == :belongs_to ? column.association.foreign_key : column.name
90
- select_options = sorted_association_options_find(column.association, false)
103
+ select_options = sorted_association_options_find(column.association, false, record)
91
104
  else
92
105
  method = column.name
93
106
  select_options = column.options[:options].collect do |text, value|
@@ -106,7 +119,7 @@ module ActiveScaffold
106
119
  if optgroup = options.delete(:optgroup)
107
120
  select(:record, method, active_scaffold_grouped_options(column, select_options, optgroup), options, html_options)
108
121
  elsif column.association
109
- collection_select(:record, method, select_options, :id, :to_label, options, html_options)
122
+ collection_select(:record, method, select_options, :id, column.options[:label_method] || :to_label, options, html_options)
110
123
  else
111
124
  select(:record, method, select_options, options, html_options)
112
125
  end
@@ -124,7 +137,7 @@ module ActiveScaffold
124
137
  select_options << [as_(:true), true]
125
138
  select_options << [as_(:false), false]
126
139
 
127
- select_tag(options[:name], options_for_select(select_options, column.column.type_cast(field_search_params[column.name])), :id => options[:id])
140
+ select_tag(options[:name], options_for_select(select_options, column.column.type_cast(options[:value])), :id => options[:id])
128
141
  end
129
142
  # we can't use checkbox ui because it's not possible to decide whether search for this field or not
130
143
  alias_method :active_scaffold_search_checkbox, :active_scaffold_search_boolean
@@ -133,29 +146,19 @@ module ActiveScaffold
133
146
  select_options = []
134
147
  select_options << [as_(:_select_), nil]
135
148
  select_options.concat ActiveScaffold::Finder::NullComparators.collect {|comp| [as_(comp), comp]}
136
- select_tag(options[:name], options_for_select(select_options, field_search_params[column.name]), :id => options[:id])
149
+ select_tag(options[:name], options_for_select(select_options, options[:value]), :id => options[:id])
137
150
  end
138
151
 
139
152
  def field_search_params_range_values(column)
140
153
  values = field_search_params[column.name]
141
- return nil if values.nil?
154
+ return nil unless values.is_a? Hash
142
155
  return values[:opt], (values[:from].blank? ? nil : values[:from]), (values[:to].blank? ? nil : values[:to])
143
-
144
156
  end
145
157
 
146
158
  def active_scaffold_search_range_string?(column)
147
159
  (column.column && column.column.text?) || column.search_ui == :string
148
160
  end
149
161
 
150
- def include_null_comparators?(column)
151
- return column.options[:null_comparators] if column.options.has_key? :null_comparators
152
- if column.association
153
- column.association.macro != :belongs_to || active_scaffold_config.columns[column.association.foreign_key].column.try(:null)
154
- else
155
- column.column.try(:null)
156
- end
157
- end
158
-
159
162
  def active_scaffold_search_range_comparator_options(column)
160
163
  select_options = ActiveScaffold::Finder::NumericComparators.collect {|comp| [as_(comp.downcase.to_sym), comp]}
161
164
  if active_scaffold_search_range_string?(column)
@@ -59,19 +59,6 @@ module ActiveScaffold
59
59
  end
60
60
  end
61
61
 
62
- # Should this column be displayed in the subform?
63
- def in_subform?(column, parent_record)
64
- return true unless column.association
65
-
66
- # Polymorphic associations can't appear because they *might* be the reverse association, and because you generally don't assign an association from the polymorphic side ... I think.
67
- return false if column.polymorphic_association?
68
-
69
- # A column shouldn't be in the subform if it's the reverse association to the parent
70
- return false if column.association.inverse_for?(parent_record.class)
71
-
72
- return true
73
- end
74
-
75
62
  def form_remote_upload_tag(url_for_options = {}, options = {})
76
63
  options[:target] = action_iframe_id(url_for_options)
77
64
  options[:multipart] ||= true
@@ -91,8 +78,8 @@ module ActiveScaffold
91
78
  # You may also flag whether the other element is visible by default or not, and the initial text will adjust accordingly.
92
79
  def link_to_visibility_toggle(id, options = {})
93
80
  options[:default_visible] = true if options[:default_visible].nil?
94
- options[:hide_label] = as_(:hide)
95
- options[:show_label] = as_(:show)
81
+ options[:hide_label] ||= as_(:hide)
82
+ options[:show_label] ||= as_(:show_block)
96
83
  javascript_tag("ActiveScaffold.create_visibility_toggle('#{id}', #{options.to_json});")
97
84
  end
98
85
 
@@ -148,7 +135,7 @@ module ActiveScaffold
148
135
  group_tag = :li
149
136
  end
150
137
  content = content_tag(group_tag, :class => (html_classes if html_classes.present?), :onclick => ('' if hover_via_click?)) do
151
- content_tag(:div, as_(link.name), :class => link.name.to_s.downcase) << content_tag(:ul, content)
138
+ content_tag(:div, as_(link.label), :class => link.name.to_s.downcase) << content_tag(:ul, content)
152
139
  end
153
140
  else
154
141
  content = render_action_link(link, record, options)
@@ -229,42 +216,61 @@ module ActiveScaffold
229
216
  end
230
217
  end
231
218
 
232
- def action_link_url(link, record)
233
- url = (@action_links_urls ||= {})[link.name_to_cache_link_url]
219
+ def is_sti_record?(record)
220
+ model = active_scaffold_config.model
221
+ is_sti_record = record && model.columns_hash.include?(model.inheritance_column) &&
222
+ record[model.inheritance_column].present? && !record.instance_of?(model)
223
+ end
224
+
225
+ def cache_action_link_url?(link, record)
226
+ active_scaffold_config.cache_action_link_urls && link.type == :member && !link.dynamic_parameters.is_a?(Proc) && !is_sti_record?(record)
227
+ end
228
+
229
+ def cached_action_link_url(link, record)
230
+ url = (@action_links_urls ||= {})[link.name_to_cache]
234
231
  url ||= begin
235
232
  url_options = action_link_url_options(link, record)
236
- if active_scaffold_config.cache_action_link_urls
237
- url = url_for(url_options)
238
- model = active_scaffold_config.model
239
- is_sti_record = record && model.columns_hash.include?(model.inheritance_column) &&
240
- record[model.inheritance_column].present?
241
- unless link.dynamic_parameters.is_a?(Proc) || is_sti_record
242
- @action_links_urls[link.name_to_cache_link_url] = url
243
- end
244
- url
233
+ if cache_action_link_url?(link, record)
234
+ @action_links_urls[link.name_to_cache] = url_for(url_options)
245
235
  else
246
236
  url_for(params_for(url_options))
247
237
  end
248
238
  end
249
-
250
- url = record ? url.sub('--ID--', record.id.to_s) : url.clone
239
+ end
240
+
241
+ def replace_id_params_in_action_link_url(link, record, url)
242
+ url = record ? url.sub('--ID--', record.to_param) : url.clone
251
243
  if link.column.try(:singular_association?)
252
- url = url.sub('--CHILD_ID--', record.send(link.column.association.name).try(:id).to_s)
244
+ child_id = record.send(link.column.association.name).try(:to_param)
245
+ if child_id.present?
246
+ url.sub!('--CHILD_ID--', child_id)
247
+ else
248
+ url.sub!(/\w+=--CHILD_ID--&?/,'')
249
+ url.sub!(/\?$/, '')
250
+ end
253
251
  elsif nested?
254
- url = url.sub('--CHILD_ID--', params[nested.param_name].to_s)
252
+ url.sub!('--CHILD_ID--', params[nested.param_name].to_s)
255
253
  end
256
-
257
- if active_scaffold_config.cache_action_link_urls
258
- query_string, non_nested_query_string = query_string_for_action_links(link)
259
- if query_string || (!link.nested_link? && non_nested_query_string)
260
- url << (url.include?('?') ? '&' : '?')
261
- url << query_string if query_string
262
- url << non_nested_query_string if !link.nested_link? && non_nested_query_string
263
- end
254
+ url
255
+ end
256
+
257
+ def add_query_string_to_cached_url(link, url)
258
+ query_string, non_nested_query_string = query_string_for_action_links(link)
259
+ nested_params = (!link.nested_link? && non_nested_query_string)
260
+ if query_string || nested_params
261
+ url << (url.include?('?') ? '&' : '?')
262
+ url << query_string if query_string
263
+ url << non_nested_query_string if nested_params
264
264
  end
265
265
  url
266
266
  end
267
267
 
268
+ def action_link_url(link, record)
269
+ url = replace_id_params_in_action_link_url(link, record, cached_action_link_url(link, record))
270
+ url = add_query_string_to_cached_url(link, url) if @action_links_urls[link.name_to_cache]
271
+ url
272
+ end
273
+
268
274
  def query_string_for_action_links(link)
269
275
  if defined?(@query_string) && link.parameters.none? { |k, v| @query_string_params.include? k }
270
276
  return [@query_string, @non_nested_query_string]
@@ -272,8 +278,8 @@ module ActiveScaffold
272
278
  keep = true
273
279
  @query_string_params ||= Set.new
274
280
  query_string_for_all = nil
275
- query_string_options = []
276
- non_nested_query_string_options = []
281
+ query_string_options = {}
282
+ non_nested_query_string_options = {}
277
283
 
278
284
  params_for.except(:controller, :action, :id).each do |key, value|
279
285
  @query_string_params << key
@@ -281,17 +287,16 @@ module ActiveScaffold
281
287
  keep = false
282
288
  next
283
289
  end
284
- qs = "#{key}=#{value}"
285
290
  if NESTED_PARAMS.include?(key) || conditions_from_params.include?(key) || (nested? && nested.param_name == key)
286
- non_nested_query_string_options << qs
291
+ non_nested_query_string_options[key] = value
287
292
  else
288
- query_string_options << qs
293
+ query_string_options[key] = value
289
294
  end
290
295
  end
291
296
 
292
- query_string = URI.escape(query_string_options.join('&')) if query_string_options.present?
297
+ query_string = query_string_options.to_query if query_string_options.present?
293
298
  if non_nested_query_string_options.present?
294
- non_nested_query_string = "#{'&' if query_string}#{URI.escape(non_nested_query_string_options.join('&'))}"
299
+ non_nested_query_string = "#{'&' if query_string}#{non_nested_query_string_options.to_query}"
295
300
  end
296
301
  if keep
297
302
  @query_string = query_string
@@ -299,8 +304,23 @@ module ActiveScaffold
299
304
  end
300
305
  [query_string, non_nested_query_string]
301
306
  end
307
+
308
+ def cache_action_link_url_options?(link, record)
309
+ active_scaffold_config.cache_action_link_urls && (link.type == :collection || !link.dynamic_parameters.is_a?(Proc)) && !is_sti_record?(record)
310
+ end
302
311
 
303
312
  def action_link_url_options(link, record)
313
+ options = (@action_links_url_options ||= {})[link.name_to_cache]
314
+ options ||= begin
315
+ options = generate_action_link_url_options(link, record)
316
+ if cache_action_link_url_options?(link, record)
317
+ @action_links_url_options[link.name_to_cache] = options
318
+ end
319
+ options
320
+ end
321
+ end
322
+
323
+ def generate_action_link_url_options(link, record)
304
324
  url_options = {:action => link.action}
305
325
  url_options[:id] = '--ID--' unless record.nil?
306
326
  url_options[:controller] = link.controller.to_s if link.controller
@@ -327,6 +347,28 @@ module ActiveScaffold
327
347
  text || options[:link]
328
348
  end
329
349
 
350
+ def replaced_action_link_url_options(link, record)
351
+ url = action_link_url_options(link, record)
352
+ url[:controller] ||= params[:controller]
353
+ missing_options, url_options = url.partition{|k,v| v.nil?}
354
+ replacements = {}
355
+ replacements['--ID--'] = record.id.to_s if record
356
+ if link.column.try(:singular_association?)
357
+ replacements['--CHILD_ID--'] = record.send(link.column.association.name).try(:id).to_s
358
+ elsif nested?
359
+ replacements['--CHILD_ID--'] = params[nested.param_name].to_s
360
+ end
361
+ url_options.collect! do |k,v|
362
+ [k.to_s, replacements[v] || v]
363
+ end
364
+ [missing_options, url_options]
365
+ end
366
+
367
+ def action_link_selected?(link, record)
368
+ missing_options, url_options = replaced_action_link_url_options(link, record)
369
+ (url_options - params.to_a).blank? && missing_options.all? {|k,v| params[k].nil?}
370
+ end
371
+
330
372
  def action_link_html_options(link, record, options)
331
373
  link_id = get_action_link_id(link, record)
332
374
  html_options = link.html_options.merge(:class => [link.html_options[:class], link.action.to_s].compact.join(' '))
@@ -344,6 +386,12 @@ module ActiveScaffold
344
386
  html_options[:data][:cancel_refresh] = true if link.refresh_on_close
345
387
  html_options[:data][:keep_open] = true if link.keep_open?
346
388
  end
389
+
390
+ if link.toggle
391
+ html_options[:class] << ' toggle'
392
+ html_options[:class] << ' active' if action_link_selected?(link, record)
393
+ end
394
+
347
395
  html_options[:target] = '_blank' if link.popup?
348
396
  html_options[:id] = link_id
349
397
  html_options[:remote] = true unless link.page? || link.popup?
@@ -360,7 +408,6 @@ module ActiveScaffold
360
408
 
361
409
  def get_action_link_id(link, record = nil, column = nil)
362
410
  column ||= link.column
363
- id = record ? record.id.to_s : (nested? ? nested.parent_id : '')
364
411
  if column && column.plural_association?
365
412
  id = "#{column.association.name}-#{record.id}"
366
413
  elsif column && column.singular_association?
@@ -370,6 +417,7 @@ module ActiveScaffold
370
417
  id = "#{column.association.name}-#{record.id}" unless record.nil?
371
418
  end
372
419
  end
420
+ id ||= record ? record.id : (nested? ? nested_parent_id : '')
373
421
  action_id = "#{id_from_controller("#{link.controller}-") if params[:parent_controller] || (link.controller && link.controller != controller.controller_path)}#{link.action}"
374
422
  action_link_id(action_id, id)
375
423
  end
@@ -416,7 +464,7 @@ module ActiveScaffold
416
464
 
417
465
  def list_row_class_method(record)
418
466
  return @_list_row_class_method if defined? @_list_row_class_method
419
- class_override_helper = :"#{clean_class_name(record.class.name)}_list_row_class"
467
+ class_override_helper = "#{clean_class_name(record.class.name)}_list_row_class"
420
468
  @_list_row_class_method = (class_override_helper if respond_to?(class_override_helper))
421
469
  end
422
470
 
@@ -487,16 +535,6 @@ module ActiveScaffold
487
535
  def format_column_calculation(column, calculation)
488
536
  "#{"#{as_(column.calculate)}: " unless column.calculate.is_a? Proc}#{format_column_value nil, column, calculation}"
489
537
  end
490
-
491
- def column_show_add_existing(column)
492
- (column.allow_add_existing and options_for_association_count(column.association) > 0)
493
- end
494
-
495
- def column_show_add_new(column, associated, record)
496
- value = (column.plural_association? && !column.readonly_association?) || (column.singular_association? and not associated.empty?)
497
- value = false unless column.association.klass.authorized_for?(:crud_type => :create)
498
- value
499
- end
500
538
 
501
539
  def clean_column_name(name)
502
540
  name.to_s.gsub('?', '')
@@ -512,11 +550,11 @@ module ActiveScaffold
512
550
  end
513
551
 
514
552
  def override_helper(column, suffix)
515
- @_override_helpers ||= {}
516
- @_override_helpers[suffix] ||= {}
517
- @_override_helpers[suffix][@record.class.name] ||= {}
518
- return @_override_helpers[suffix][@record.class.name][column.name] if @_override_helpers[suffix][@record.class.name].include? column.name
519
- @_override_helpers[suffix][@record.class.name][column.name] = begin
553
+ hash = @_override_helpers ||= {}
554
+ hash = hash[suffix] ||= {}
555
+ hash = hash[column.active_record_class.name] ||= {}
556
+ return hash[column.name] if hash.include? column.name
557
+ hash[column.name] = begin
520
558
  method_with_class = override_helper_name(column, suffix, true)
521
559
  if respond_to?(method_with_class)
522
560
  method_with_class
@@ -530,7 +568,7 @@ module ActiveScaffold
530
568
  def display_message(message)
531
569
  if (highlights = active_scaffold_config.highlight_messages)
532
570
  message = highlights.inject(message) do |msg, (phrases, highlighter)|
533
- highlight(msg, phrases, highlighter)
571
+ highlight(msg, phrases, highlighter || {})
534
572
  end
535
573
  end
536
574
  if (format = active_scaffold_config.timestamped_messages)