active_scaffold 3.2.18 → 3.2.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (246) hide show
  1. data/CHANGELOG +3 -0
  2. data/app/assets/images/active_scaffold/add.png +0 -0
  3. data/app/assets/images/active_scaffold/arrow_down.png +0 -0
  4. data/app/assets/images/active_scaffold/arrow_up.png +0 -0
  5. data/app/assets/images/active_scaffold/close.png +0 -0
  6. data/app/assets/images/active_scaffold/close_touch.png +0 -0
  7. data/app/assets/images/active_scaffold/config.png +0 -0
  8. data/app/assets/images/active_scaffold/cross.png +0 -0
  9. data/app/assets/images/active_scaffold/gears.png +0 -0
  10. data/app/assets/images/active_scaffold/indicator-small.gif +0 -0
  11. data/app/assets/images/active_scaffold/indicator.gif +0 -0
  12. data/app/assets/images/active_scaffold/magnifier.png +0 -0
  13. data/app/assets/javascripts/active_scaffold.js.erb +19 -0
  14. data/app/assets/javascripts/jquery/active_scaffold.js +1113 -0
  15. data/app/assets/javascripts/jquery/date_picker_bridge.js.erb +24 -0
  16. data/app/assets/javascripts/jquery/draggable_lists.js +27 -0
  17. data/app/assets/javascripts/jquery/jquery.editinplace.js +743 -0
  18. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +7 -0
  19. data/app/assets/javascripts/prototype/active_scaffold.js +1107 -0
  20. data/app/assets/javascripts/prototype/dhtml_history.js +870 -0
  21. data/app/assets/javascripts/prototype/form_enhancements.js +117 -0
  22. data/app/assets/javascripts/prototype/rico_corner.js +370 -0
  23. data/app/assets/javascripts/prototype/tiny_mce_bridge.js +7 -0
  24. data/app/assets/stylesheets/active_scaffold-ie.css.scss +54 -0
  25. data/app/assets/stylesheets/active_scaffold.css.scss +14 -0
  26. data/app/assets/stylesheets/active_scaffold_colors.css.scss +395 -0
  27. data/app/assets/stylesheets/active_scaffold_extensions.css.erb +2 -0
  28. data/app/assets/stylesheets/active_scaffold_images.css.scss +40 -0
  29. data/app/assets/stylesheets/active_scaffold_layout.css +936 -0
  30. data/app/assets/stylesheets/blue-theme.css +74 -0
  31. data/config/locales/de.yml +125 -0
  32. data/config/locales/en.yml +127 -0
  33. data/config/locales/es.yml +128 -0
  34. data/config/locales/fr.yml +131 -0
  35. data/config/locales/hu.yml +126 -0
  36. data/config/locales/ja.yml +126 -0
  37. data/config/locales/ru.yml +135 -0
  38. data/frontends/default/views/_action_group.html.erb +24 -0
  39. data/frontends/default/views/_add_existing_form.html.erb +30 -0
  40. data/frontends/default/views/_base_form.html.erb +53 -0
  41. data/frontends/default/views/_create_form.html.erb +8 -0
  42. data/frontends/default/views/_create_form_on_list.html.erb +6 -0
  43. data/frontends/default/views/_field_search.html.erb +32 -0
  44. data/frontends/default/views/_form.html.erb +28 -0
  45. data/frontends/default/views/_form_association.html.erb +17 -0
  46. data/frontends/default/views/_form_association_footer.html.erb +47 -0
  47. data/frontends/default/views/_form_attribute.html.erb +23 -0
  48. data/frontends/default/views/_form_hidden_attribute.html.erb +7 -0
  49. data/frontends/default/views/_form_messages.html.erb +5 -0
  50. data/frontends/default/views/_horizontal_subform.html.erb +22 -0
  51. data/frontends/default/views/_horizontal_subform_footer.html.erb +0 -0
  52. data/frontends/default/views/_horizontal_subform_header.html.erb +11 -0
  53. data/frontends/default/views/_horizontal_subform_record.html.erb +43 -0
  54. data/frontends/default/views/_human_conditions.html.erb +1 -0
  55. data/frontends/default/views/_list.html.erb +18 -0
  56. data/frontends/default/views/_list_actions.html.erb +15 -0
  57. data/frontends/default/views/_list_calculations.html.erb +16 -0
  58. data/frontends/default/views/_list_column_headings.html.erb +12 -0
  59. data/frontends/default/views/_list_header.html.erb +10 -0
  60. data/frontends/default/views/_list_inline_adapter.html.erb +21 -0
  61. data/frontends/default/views/_list_messages.html.erb +28 -0
  62. data/frontends/default/views/_list_pagination.html.erb +11 -0
  63. data/frontends/default/views/_list_pagination_links.html.erb +9 -0
  64. data/frontends/default/views/_list_record.html.erb +13 -0
  65. data/frontends/default/views/_list_record_columns.html.erb +8 -0
  66. data/frontends/default/views/_list_with_header.html.erb +36 -0
  67. data/frontends/default/views/_messages.html.erb +10 -0
  68. data/frontends/default/views/_refresh_list.js.erb +1 -0
  69. data/frontends/default/views/_render_field.js.erb +20 -0
  70. data/frontends/default/views/_row.html.erb +1 -0
  71. data/frontends/default/views/_search.html.erb +34 -0
  72. data/frontends/default/views/_search_attribute.html.erb +10 -0
  73. data/frontends/default/views/_show.html.erb +8 -0
  74. data/frontends/default/views/_show_columns.html.erb +15 -0
  75. data/frontends/default/views/_update_actions.html.erb +9 -0
  76. data/frontends/default/views/_update_calculations.js.erb +4 -0
  77. data/frontends/default/views/_update_form.html.erb +6 -0
  78. data/frontends/default/views/_update_messages.js.erb +2 -0
  79. data/frontends/default/views/_vertical_subform.html.erb +12 -0
  80. data/frontends/default/views/_vertical_subform_record.html.erb +43 -0
  81. data/frontends/default/views/action_confirmation.html.erb +13 -0
  82. data/frontends/default/views/add_existing.js.erb +14 -0
  83. data/frontends/default/views/add_existing_form.html.erb +5 -0
  84. data/frontends/default/views/create.html.erb +5 -0
  85. data/frontends/default/views/delete.html.erb +13 -0
  86. data/frontends/default/views/destroy.js.erb +26 -0
  87. data/frontends/default/views/edit_associated.js.erb +12 -0
  88. data/frontends/default/views/field_search.html.erb +5 -0
  89. data/frontends/default/views/form_messages.js.erb +1 -0
  90. data/frontends/default/views/list.html.erb +1 -0
  91. data/frontends/default/views/on_action_update.js.erb +22 -0
  92. data/frontends/default/views/on_create.js.erb +38 -0
  93. data/frontends/default/views/on_mark.js.erb +6 -0
  94. data/frontends/default/views/on_update.js.erb +29 -0
  95. data/frontends/default/views/refresh_list.js.erb +2 -0
  96. data/frontends/default/views/render_field.js.erb +1 -0
  97. data/frontends/default/views/row.js.erb +2 -0
  98. data/frontends/default/views/search.html.erb +5 -0
  99. data/frontends/default/views/show.html.erb +5 -0
  100. data/frontends/default/views/update.html.erb +8 -0
  101. data/frontends/default/views/update_column.js.erb +15 -0
  102. data/frontends/default/views/update_row.js.erb +1 -0
  103. data/lib/active_scaffold/actions/common_search.rb +22 -0
  104. data/lib/active_scaffold/actions/core.rb +203 -0
  105. data/lib/active_scaffold/actions/create.rb +139 -0
  106. data/lib/active_scaffold/actions/delete.rb +74 -0
  107. data/lib/active_scaffold/actions/field_search.rb +78 -0
  108. data/lib/active_scaffold/actions/list.rb +208 -0
  109. data/lib/active_scaffold/actions/mark.rb +89 -0
  110. data/lib/active_scaffold/actions/nested.rb +244 -0
  111. data/lib/active_scaffold/actions/search.rb +48 -0
  112. data/lib/active_scaffold/actions/show.rb +61 -0
  113. data/lib/active_scaffold/actions/subform.rb +23 -0
  114. data/lib/active_scaffold/actions/update.rb +156 -0
  115. data/lib/active_scaffold/active_record_permissions.rb +135 -0
  116. data/lib/active_scaffold/attribute_params.rb +200 -0
  117. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +39 -0
  118. data/lib/active_scaffold/bridges/ancestry.rb +5 -0
  119. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +37 -0
  120. data/lib/active_scaffold/bridges/bitfields.rb +6 -0
  121. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +66 -0
  122. data/lib/active_scaffold/bridges/calendar_date_select.rb +24 -0
  123. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +127 -0
  124. data/lib/active_scaffold/bridges/cancan.rb +15 -0
  125. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +31 -0
  126. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge_helpers.rb +10 -0
  127. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +45 -0
  128. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +17 -0
  129. data/lib/active_scaffold/bridges/carrierwave.rb +12 -0
  130. data/lib/active_scaffold/bridges/country_helper/country_helper_bridge.rb +358 -0
  131. data/lib/active_scaffold/bridges/country_helper.rb +9 -0
  132. data/lib/active_scaffold/bridges/date_picker/ext.rb +63 -0
  133. data/lib/active_scaffold/bridges/date_picker/helper.rb +180 -0
  134. data/lib/active_scaffold/bridges/date_picker.rb +23 -0
  135. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge.rb +34 -0
  136. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge_helpers.rb +10 -0
  137. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +27 -0
  138. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +16 -0
  139. data/lib/active_scaffold/bridges/dragonfly.rb +9 -0
  140. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +46 -0
  141. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +57 -0
  142. data/lib/active_scaffold/bridges/file_column/form_ui.rb +34 -0
  143. data/lib/active_scaffold/bridges/file_column/list_ui.rb +26 -0
  144. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +43 -0
  145. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +9 -0
  146. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +15 -0
  147. data/lib/active_scaffold/bridges/file_column.rb +11 -0
  148. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +27 -0
  149. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +16 -0
  150. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +36 -0
  151. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +24 -0
  152. data/lib/active_scaffold/bridges/paperclip.rb +12 -0
  153. data/lib/active_scaffold/bridges/record_select/helpers.rb +92 -0
  154. data/lib/active_scaffold/bridges/record_select.rb +11 -0
  155. data/lib/active_scaffold/bridges/semantic_attributes/column.rb +20 -0
  156. data/lib/active_scaffold/bridges/semantic_attributes.rb +5 -0
  157. data/lib/active_scaffold/bridges/shared/date_bridge.rb +209 -0
  158. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +46 -0
  159. data/lib/active_scaffold/bridges/tiny_mce.rb +17 -0
  160. data/lib/active_scaffold/bridges.rb +61 -0
  161. data/lib/active_scaffold/config/base.rb +75 -0
  162. data/lib/active_scaffold/config/core.rb +236 -0
  163. data/lib/active_scaffold/config/create.rb +32 -0
  164. data/lib/active_scaffold/config/delete.rb +32 -0
  165. data/lib/active_scaffold/config/field_search.rb +79 -0
  166. data/lib/active_scaffold/config/form.rb +64 -0
  167. data/lib/active_scaffold/config/list.rb +247 -0
  168. data/lib/active_scaffold/config/mark.rb +30 -0
  169. data/lib/active_scaffold/config/nested.rb +42 -0
  170. data/lib/active_scaffold/config/search.rb +73 -0
  171. data/lib/active_scaffold/config/show.rb +31 -0
  172. data/lib/active_scaffold/config/subform.rb +35 -0
  173. data/lib/active_scaffold/config/update.rb +33 -0
  174. data/lib/active_scaffold/configurable.rb +29 -0
  175. data/lib/active_scaffold/constraints.rb +171 -0
  176. data/lib/active_scaffold/data_structures/action_columns.rb +142 -0
  177. data/lib/active_scaffold/data_structures/action_link.rb +185 -0
  178. data/lib/active_scaffold/data_structures/action_links.rb +191 -0
  179. data/lib/active_scaffold/data_structures/actions.rb +45 -0
  180. data/lib/active_scaffold/data_structures/bridge.rb +22 -0
  181. data/lib/active_scaffold/data_structures/column.rb +402 -0
  182. data/lib/active_scaffold/data_structures/columns.rb +75 -0
  183. data/lib/active_scaffold/data_structures/error_message.rb +24 -0
  184. data/lib/active_scaffold/data_structures/nested_info.rb +171 -0
  185. data/lib/active_scaffold/data_structures/set.rb +61 -0
  186. data/lib/active_scaffold/data_structures/sorting.rb +167 -0
  187. data/lib/active_scaffold/engine.rb +4 -0
  188. data/lib/active_scaffold/extensions/action_controller_rendering.rb +20 -0
  189. data/lib/active_scaffold/extensions/action_controller_rescueing.rb +7 -0
  190. data/lib/active_scaffold/extensions/action_view_rendering.rb +115 -0
  191. data/lib/active_scaffold/extensions/active_record_offset.rb +12 -0
  192. data/lib/active_scaffold/extensions/array.rb +7 -0
  193. data/lib/active_scaffold/extensions/cache_association.rb +16 -0
  194. data/lib/active_scaffold/extensions/localize.rb +10 -0
  195. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +12 -0
  196. data/lib/active_scaffold/extensions/nil_id_in_url_params.rb +7 -0
  197. data/lib/active_scaffold/extensions/paginator_extensions.rb +26 -0
  198. data/lib/active_scaffold/extensions/reverse_associations.rb +64 -0
  199. data/lib/active_scaffold/extensions/routing_mapper.rb +48 -0
  200. data/lib/active_scaffold/extensions/to_label.rb +8 -0
  201. data/lib/active_scaffold/extensions/unsaved_associated.rb +61 -0
  202. data/lib/active_scaffold/extensions/unsaved_record.rb +20 -0
  203. data/lib/active_scaffold/extensions/usa_state.rb +46 -0
  204. data/lib/active_scaffold/finder.rb +399 -0
  205. data/lib/active_scaffold/helpers/association_helpers.rb +42 -0
  206. data/lib/active_scaffold/helpers/controller_helpers.rb +94 -0
  207. data/lib/active_scaffold/helpers/form_column_helpers.rb +322 -0
  208. data/lib/active_scaffold/helpers/human_condition_helpers.rb +64 -0
  209. data/lib/active_scaffold/helpers/id_helpers.rb +131 -0
  210. data/lib/active_scaffold/helpers/list_column_helpers.rb +374 -0
  211. data/lib/active_scaffold/helpers/pagination_helpers.rb +62 -0
  212. data/lib/active_scaffold/helpers/search_column_helpers.rb +257 -0
  213. data/lib/active_scaffold/helpers/show_column_helpers.rb +44 -0
  214. data/lib/active_scaffold/helpers/view_helpers.rb +398 -0
  215. data/lib/active_scaffold/marked_model.rb +38 -0
  216. data/lib/active_scaffold/paginator.rb +136 -0
  217. data/lib/active_scaffold/responds_to_parent.rb +70 -0
  218. data/lib/active_scaffold/tableless.rb +83 -0
  219. data/lib/active_scaffold/version.rb +9 -0
  220. data/lib/active_scaffold.rb +373 -0
  221. data/lib/active_scaffold_env.rb +13 -0
  222. data/lib/generators/active_scaffold/USAGE +29 -0
  223. data/lib/generators/active_scaffold/active_scaffold_generator.rb +21 -0
  224. data/lib/generators/active_scaffold_controller/USAGE +19 -0
  225. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +29 -0
  226. data/lib/generators/active_scaffold_controller/templates/controller.rb +4 -0
  227. data/lib/generators/active_scaffold_controller/templates/helper.rb +2 -0
  228. data/public/blank.html +33 -0
  229. data/shoulda_macros/macros.rb +136 -0
  230. data/vendor/assets/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  231. data/vendor/assets/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  232. data/vendor/assets/images/ui-bg_flat_10_000000_40x100.png +0 -0
  233. data/vendor/assets/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  234. data/vendor/assets/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  235. data/vendor/assets/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  236. data/vendor/assets/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  237. data/vendor/assets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  238. data/vendor/assets/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  239. data/vendor/assets/images/ui-icons_222222_256x240.png +0 -0
  240. data/vendor/assets/images/ui-icons_228ef1_256x240.png +0 -0
  241. data/vendor/assets/images/ui-icons_ef8c08_256x240.png +0 -0
  242. data/vendor/assets/images/ui-icons_ffd27a_256x240.png +0 -0
  243. data/vendor/assets/images/ui-icons_ffffff_256x240.png +0 -0
  244. data/vendor/assets/javascripts/jquery-ui-timepicker-addon.js +1276 -0
  245. data/vendor/assets/stylesheets/jquery-ui.css +568 -0
  246. metadata +261 -17
@@ -0,0 +1,399 @@
1
+ module ActiveScaffold
2
+ module Finder
3
+ def self.like_operator
4
+ @@like_operator ||= ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
5
+ end
6
+
7
+ module ClassMethods
8
+ # Takes a collection of search terms (the tokens) and creates SQL that
9
+ # searches all specified ActiveScaffold columns. A row will match if each
10
+ # token is found in at least one of the columns.
11
+ def create_conditions_for_columns(tokens, columns, text_search = :full)
12
+ # if there aren't any columns, then just return a nil condition
13
+ return unless columns.length > 0
14
+ like_pattern = like_pattern(text_search)
15
+
16
+ tokens = [tokens] if tokens.is_a? String
17
+
18
+ where_clauses = []
19
+ columns.each do |column|
20
+ Array(column.search_sql).each do |search_sql|
21
+ where_clauses << "#{search_sql} #{(column.column.nil? || column.column.text?) ? ActiveScaffold::Finder.like_operator : '='} ?"
22
+ end
23
+ end
24
+ phrase = where_clauses.join(' OR ')
25
+
26
+ tokens.collect do |value|
27
+ columns.inject([phrase]) do |condition, column|
28
+ Array(column.search_sql).size.times do
29
+ condition.push((column.column.nil? || column.column.text?) ? like_pattern.sub('?', value) : column.column.type_cast(value))
30
+ end
31
+ condition
32
+ end
33
+ end
34
+ end
35
+
36
+ # Generates an SQL condition for the given ActiveScaffold column based on
37
+ # that column's database type (or form_ui ... for virtual columns?).
38
+ # TODO: this should reside on the column, not the controller
39
+ def condition_for_column(column, value, text_search = :full)
40
+ like_pattern = like_pattern(text_search)
41
+ if self.respond_to?("condition_for_#{column.name}_column")
42
+ return self.send("condition_for_#{column.name}_column", column, value, like_pattern)
43
+ end
44
+ return unless column and column.search_sql and not value.blank?
45
+ search_ui = column.search_ui || column.column.try(:type)
46
+ begin
47
+ sql, *values = if search_ui && self.respond_to?("condition_for_#{search_ui}_type")
48
+ self.send("condition_for_#{search_ui}_type", column, value, like_pattern)
49
+ else
50
+ if column.search_sql.instance_of? Proc
51
+ column.search_sql.call(value)
52
+ else
53
+ case search_ui
54
+ when :boolean, :checkbox
55
+ ["%{search_sql} = ?", column.column ? column.column.type_cast(value) : value]
56
+ when :integer, :decimal, :float
57
+ condition_for_numeric(column, value)
58
+ when :string, :range
59
+ condition_for_range(column, value, like_pattern)
60
+ when :date, :time, :datetime, :timestamp
61
+ condition_for_datetime(column, value)
62
+ when :select, :multi_select, :country, :usa_state, :chosen, :multi_chosen
63
+ ["%{search_sql} in (?)", Array(value)]
64
+ else
65
+ if column.column.nil? || column.column.text?
66
+ ["%{search_sql} #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
67
+ else
68
+ ["%{search_sql} = ?", column.column.type_cast(value)]
69
+ end
70
+ end
71
+ end
72
+ end
73
+ return nil unless sql
74
+
75
+ conditions = [column.search_sql.collect { |search_sql| sql % {:search_sql => search_sql} }.join(' OR ')]
76
+ conditions += values*column.search_sql.size if values.present?
77
+ conditions
78
+ rescue Exception => e
79
+ logger.error Time.now.to_s + "#{e.inspect} -- on the ActiveScaffold column :#{column.name}, search_ui = #{search_ui} in #{self.name}"
80
+ raise e
81
+ end
82
+ end
83
+
84
+ def condition_for_numeric(column, value)
85
+ if !value.is_a?(Hash)
86
+ ["%{search_sql} = ?", condition_value_for_numeric(column, value)]
87
+ elsif ActiveScaffold::Finder::NullComparators.include?(value[:opt])
88
+ condition_for_null_type(column, value[:opt])
89
+ elsif value[:from].blank? or not ActiveScaffold::Finder::NumericComparators.include?(value[:opt])
90
+ nil
91
+ elsif value[:opt] == 'BETWEEN'
92
+ ["(%{search_sql} BETWEEN ? AND ?)", condition_value_for_numeric(column, value[:from]), condition_value_for_numeric(column, value[:to])]
93
+ else
94
+ ["%{search_sql} #{value[:opt]} ?", condition_value_for_numeric(column, value[:from])]
95
+ end
96
+ end
97
+
98
+ def condition_for_range(column, value, like_pattern = nil)
99
+ if !value.is_a?(Hash)
100
+ if column.column.nil? || column.column.text?
101
+ ["%{search_sql} #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
102
+ else
103
+ ["%{search_sql} = ?", column.column.type_cast(value)]
104
+ end
105
+ elsif ActiveScaffold::Finder::NullComparators.include?(value[:opt])
106
+ condition_for_null_type(column, value[:opt], like_pattern)
107
+ elsif value[:from].blank?
108
+ nil
109
+ elsif ActiveScaffold::Finder::StringComparators.values.include?(value[:opt])
110
+ ["%{search_sql} #{ActiveScaffold::Finder.like_operator} ?", value[:opt].sub('?', value[:from])]
111
+ elsif value[:opt] == 'BETWEEN'
112
+ ["(%{search_sql} BETWEEN ? AND ?)", value[:from], value[:to]]
113
+ elsif ActiveScaffold::Finder::NumericComparators.include?(value[:opt])
114
+ ["%{search_sql} #{value[:opt]} ?", value[:from]]
115
+ else
116
+ nil
117
+ end
118
+ end
119
+
120
+ def condition_value_for_datetime(value, conversion = :to_time)
121
+ if value.is_a? Hash
122
+ Time.zone.local(*[:year, :month, :day, :hour, :minute, :second].collect {|part| value[part].to_i}) rescue nil
123
+ elsif value.respond_to?(:strftime)
124
+ if conversion == :to_time
125
+ # Explicitly get the current zone, because TimeWithZone#to_time in rails 3.2.3 returns UTC.
126
+ # https://github.com/rails/rails/pull/2453
127
+ value.to_time.in_time_zone
128
+ else
129
+ value.send(conversion)
130
+ end
131
+ elsif conversion == :to_date
132
+ Date.strptime(value, I18n.t('date.formats.default')) rescue nil
133
+ else
134
+ parts = Date._parse(value)
135
+ format = I18n.translate 'time.formats.picker', :default => '' if ActiveScaffold.js_framework == :jquery
136
+ if format.blank?
137
+ time_parts = [[:hour, '%H'], [:min, '%M'], [:sec, '%S']].collect {|part, format_part| format_part if parts[part].present?}.compact
138
+ format = "#{I18n.t('date.formats.default')} #{time_parts.join(':')} #{'%z' if parts[:offset].present?}"
139
+ else
140
+ format += ' %z' if parts[:offset].present? && format !~ /%z/i
141
+ end
142
+ time = DateTime.strptime(value, format)
143
+ time = Time.zone.local_to_utc(time).in_time_zone unless parts[:offset]
144
+ time = time.send(conversion) unless conversion == :to_time
145
+ time
146
+ end unless value.nil? || value.blank?
147
+ end
148
+
149
+ def condition_value_for_numeric(column, value)
150
+ return value if value.nil?
151
+ value = i18n_number_to_native_format(value) if [:i18n_number, :currency].include?(column.options[:format])
152
+ case (column.search_ui || column.column.type)
153
+ when :integer then value.to_i rescue value ? 1 : 0
154
+ when :float then value.to_f
155
+ when :decimal then ActiveRecord::ConnectionAdapters::Column.value_to_decimal(value)
156
+ else
157
+ value
158
+ end
159
+ end
160
+
161
+ def i18n_number_to_native_format(value)
162
+ native = '.'
163
+ delimiter = I18n.t('number.format.delimiter')
164
+ separator = I18n.t('number.format.separator')
165
+ return value if value.blank? || !value.is_a?(String)
166
+ unless delimiter == native && !value.include?(separator) && value !~ /\.\d{3}$/
167
+ value.gsub(/[^0-9\-#{I18n.t('number.format.separator')}]/, '').gsub(I18n.t('number.format.separator'), native)
168
+ else
169
+ value
170
+ end
171
+ end
172
+
173
+ def datetime_conversion_for_condition(column)
174
+ if column.column
175
+ column.column.type == :date ? :to_date : :to_time
176
+ else
177
+ :to_time
178
+ end
179
+ end
180
+
181
+ def condition_for_datetime(column, value, like_pattern = nil)
182
+ conversion = datetime_conversion_for_condition(column)
183
+ from_value = condition_value_for_datetime(value[:from], conversion)
184
+ to_value = condition_value_for_datetime(value[:to], conversion)
185
+
186
+ if from_value.nil? and to_value.nil?
187
+ nil
188
+ elsif !from_value
189
+ ["%{search_sql} <= ?", to_value.to_s(:db)]
190
+ elsif !to_value
191
+ ["%{search_sql} >= ?", from_value.to_s(:db)]
192
+ else
193
+ ["%{search_sql} BETWEEN ? AND ?", from_value.to_s(:db), to_value.to_s(:db)]
194
+ end
195
+ end
196
+
197
+ def condition_for_record_select_type(column, value, like_pattern = nil)
198
+ if value.is_a?(Array)
199
+ ["%{search_sql} IN (?)", value]
200
+ else
201
+ ["%{search_sql} = ?", value]
202
+ end
203
+ end
204
+
205
+ def condition_for_null_type(column, value, like_pattern = nil)
206
+ case value.to_sym
207
+ when :null
208
+ ["%{search_sql} is null", []]
209
+ when :not_null
210
+ ["%{search_sql} is not null", []]
211
+ else
212
+ nil
213
+ end
214
+ end
215
+
216
+ def like_pattern(text_search)
217
+ case text_search
218
+ when :full then '%?%'
219
+ when :start then '?%'
220
+ when :end then '%?'
221
+ else '?'
222
+ end
223
+ end
224
+ end
225
+
226
+ NumericComparators = [
227
+ '=',
228
+ '>=',
229
+ '<=',
230
+ '>',
231
+ '<',
232
+ '!=',
233
+ 'BETWEEN'
234
+ ]
235
+ StringComparators = {
236
+ :contains => '%?%',
237
+ :begins_with => '?%',
238
+ :ends_with => '%?'
239
+ }
240
+ NullComparators = [
241
+ 'null',
242
+ 'not_null'
243
+ ]
244
+
245
+
246
+
247
+ def self.included(klass)
248
+ klass.extend ClassMethods
249
+ end
250
+
251
+ protected
252
+
253
+ attr_writer :active_scaffold_conditions
254
+ def active_scaffold_conditions
255
+ @active_scaffold_conditions ||= []
256
+ end
257
+
258
+ attr_writer :active_scaffold_includes
259
+ def active_scaffold_includes
260
+ @active_scaffold_includes ||= []
261
+ end
262
+
263
+ attr_writer :active_scaffold_habtm_joins
264
+ def active_scaffold_habtm_joins
265
+ @active_scaffold_habtm_joins ||= []
266
+ end
267
+
268
+ def all_conditions
269
+ [
270
+ active_scaffold_conditions, # from the search modules
271
+ conditions_for_collection, # from the dev
272
+ conditions_from_params, # from the parameters (e.g. /users/list?first_name=Fred)
273
+ conditions_from_constraints, # from any constraints (embedded scaffolds)
274
+ active_scaffold_session_storage[:conditions] # embedding conditions (weaker constraints)
275
+ ]
276
+ end
277
+
278
+ # returns a single record (the given id) but only if it's allowed for the specified action.
279
+ # accomplishes this by checking model.#{action}_authorized?
280
+ # TODO: this should reside on the model, not the controller
281
+ def find_if_allowed(id, crud_type, klass = beginning_of_chain)
282
+ record = klass.find(id)
283
+ raise ActiveScaffold::RecordNotAllowed, "#{klass} with id = #{id}" unless record.authorized_for?(:crud_type => crud_type.to_sym)
284
+ return record
285
+ end
286
+ # valid options may include:
287
+ # * :sorting - a Sorting DataStructure (basically an array of hashes of field => direction, e.g. [{:field1 => 'asc'}, {:field2 => 'desc'}]). please note that multi-column sorting has some limitations: if any column in a multi-field sort uses method-based sorting, it will be ignored. method sorting only works for single-column sorting.
288
+ # * :per_page
289
+ # * :page
290
+ def finder_options(options = {})
291
+ search_conditions = all_conditions
292
+ full_includes = (active_scaffold_includes.blank? ? nil : active_scaffold_includes)
293
+
294
+ # create a general-use options array that's compatible with Rails finders
295
+ finder_options = { :reorder => options[:sorting].try(:clause),
296
+ :conditions => search_conditions,
297
+ :joins => joins_for_finder,
298
+ :includes => full_includes}
299
+
300
+ finder_options.merge! custom_finder_options
301
+ finder_options
302
+ end
303
+
304
+ def count_items(find_options = {}, count_includes = nil)
305
+ count_includes ||= find_options[:includes] unless find_options[:conditions].nil?
306
+ options = find_options.reject{|k,v| [:select, :reorder].include? k}
307
+ options[:includes] = count_includes
308
+
309
+ # NOTE: we must use :include in the count query, because some conditions may reference other tables
310
+ count_query = append_to_query(beginning_of_chain, options)
311
+ count = count_query.count
312
+
313
+ # Converts count to an integer if ActiveRecord returned an OrderedHash
314
+ # that happens when find_options contains a :group key
315
+ count = count.length if count.is_a? ActiveSupport::OrderedHash
316
+ count
317
+ end
318
+
319
+ # returns a Paginator::Page (not from ActiveRecord::Paginator) for the given parameters
320
+ # See finder_options for valid options
321
+ def find_page(options = {})
322
+ options.assert_valid_keys :sorting, :per_page, :page, :count_includes, :pagination
323
+ options[:per_page] ||= 999999999
324
+ options[:page] ||= 1
325
+
326
+ find_options = finder_options(options)
327
+
328
+ # NOTE: we must use :include in the count query, because some conditions may reference other tables
329
+ if options[:pagination] && options[:pagination] != :infinite
330
+ count = count_items(find_options, options[:count_includes])
331
+ end
332
+
333
+ klass = beginning_of_chain
334
+ # we build the paginator differently for method- and sql-based sorting
335
+ if options[:sorting] and options[:sorting].sorts_by_method?
336
+ pager = ::Paginator.new(count, options[:per_page]) do |offset, per_page|
337
+ sorted_collection = sort_collection_by_column(append_to_query(klass, find_options).all, *options[:sorting].first)
338
+ sorted_collection = sorted_collection.slice(offset, per_page) if options[:pagination]
339
+ sorted_collection
340
+ end
341
+ else
342
+ pager = ::Paginator.new(count, options[:per_page]) do |offset, per_page|
343
+ find_options.merge!(:offset => offset, :limit => per_page) if options[:pagination]
344
+ append_to_query(klass, find_options).all
345
+ end
346
+ end
347
+ pager.page(options[:page])
348
+ end
349
+
350
+ def calculate(column)
351
+ conditions = all_conditions
352
+ includes = active_scaffold_config.list.count_includes
353
+ includes ||= active_scaffold_includes unless conditions.nil?
354
+ append_to_query(beginning_of_chain, :conditions => conditions, :includes => includes,
355
+ :joins => joins_for_collection).calculate(column.calculate, column.name)
356
+ end
357
+
358
+ def append_to_query(query, options)
359
+ options.assert_valid_keys :where, :select, :group, :reorder, :limit, :offset, :joins, :includes, :lock, :readonly, :from, :conditions
360
+ query = apply_conditions(query, *options.delete(:conditions)) if options[:conditions]
361
+ options.reject{|k, v| v.blank?}.inject(query) do |query, (k, v)|
362
+ query.send((k.to_sym), v)
363
+ end
364
+ end
365
+
366
+ def joins_for_finder
367
+ case joins_for_collection
368
+ when String
369
+ [ joins_for_collection ]
370
+ when Array
371
+ joins_for_collection
372
+ else
373
+ []
374
+ end + active_scaffold_habtm_joins
375
+ end
376
+
377
+ def apply_conditions(query, *conditions)
378
+ conditions.reject(&:blank?).inject(query) do |query, condition|
379
+ if condition.is_a?(Array) && !condition.first.is_a?(String) # multiple conditions
380
+ apply_conditions(query, *condition)
381
+ else
382
+ query.where(condition)
383
+ end
384
+ end
385
+ end
386
+
387
+ # TODO: this should reside on the column, not the controller
388
+ def sort_collection_by_column(collection, column, order)
389
+ sorter = column.sort[:method]
390
+ collection = collection.sort_by { |record|
391
+ value = (sorter.is_a? Proc) ? record.instance_eval(&sorter) : record.instance_eval(sorter)
392
+ value = '' if value.nil?
393
+ value
394
+ }
395
+ collection.reverse! if order.downcase == 'desc'
396
+ collection
397
+ end
398
+ end
399
+ end
@@ -0,0 +1,42 @@
1
+ module ActiveScaffold
2
+ module Helpers
3
+ module AssociationHelpers
4
+ # Provides a way to honor the :conditions on an association while searching the association's klass
5
+ def association_options_find(association, conditions = nil)
6
+ relation = association.klass.where(conditions).where(association.options[:conditions])
7
+ relation = relation.includes(association.options[:include]) if association.options[:include]
8
+ relation.all
9
+ end
10
+
11
+ def association_options_count(association, conditions = nil)
12
+ association.klass.where(conditions).where(association.options[:conditions]).count
13
+ end
14
+
15
+ # returns options for the given association as a collection of [id, label] pairs intended for the +options_for_select+ helper.
16
+ def options_for_association(association, include_all = false)
17
+ available_records = association_options_find(association, include_all ? nil : options_for_association_conditions(association))
18
+ available_records ||= []
19
+ available_records.sort{|a,b| a.to_label <=> b.to_label}.collect { |model| [ model.to_label, model.id ] }
20
+ end
21
+
22
+ def options_for_association_count(association)
23
+ association_options_count(association, options_for_association_conditions(association))
24
+ end
25
+
26
+ # A useful override for customizing the records present in an association dropdown.
27
+ # Should work in both the subform and form_ui=>:select modes.
28
+ # Check association.name to specialize the conditions per-column.
29
+ def options_for_association_conditions(association)
30
+ return nil if association.options[:through]
31
+ case association.macro
32
+ when :has_one, :has_many
33
+ # Find only orphaned objects
34
+ "#{association.foreign_key} IS NULL"
35
+ when :belongs_to, :has_and_belongs_to_many
36
+ # Find all
37
+ nil
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,94 @@
1
+ module ActiveScaffold
2
+ module Helpers
3
+ module ControllerHelpers
4
+ def self.included(controller)
5
+ controller.class_eval { helper_method :params_for, :params_conditions, :main_path_to_return, :render_parent?, :render_parent_options, :render_parent_action, :nested_singular_association?, :build_associated}
6
+ end
7
+
8
+ include ActiveScaffold::Helpers::IdHelpers
9
+
10
+ def params_conditions
11
+ conditions_from_params.keys
12
+ end
13
+
14
+ def params_for(options = {})
15
+ # :adapter and :position are one-use rendering arguments. they should not propagate.
16
+ # :sort, :sort_direction, and :page are arguments that stored in the session. they need not propagate.
17
+ # and wow. no we don't want to propagate :record.
18
+ # :commit is a special rails variable for form buttons
19
+ blacklist = [:adapter, :position, :sort, :sort_direction, :page, :record, :commit, :_method, :authenticity_token, :iframe]
20
+ unless @params_for
21
+ @params_for = {}
22
+ params.select { |key, value| blacklist.exclude? key.to_sym if key }.each {|key, value| @params_for[key.to_sym] = value.duplicable? ? value.clone : value}
23
+ @params_for[:controller] = '/' + @params_for[:controller].to_s unless @params_for[:controller].to_s.first(1) == '/' # for namespaced controllers
24
+ @params_for.delete(:id) if @params_for[:id].nil?
25
+ end
26
+ @params_for.merge(options)
27
+ end
28
+
29
+ # Parameters to generate url to the main page (override if the ActiveScaffold is used as a component on another controllers page)
30
+ def main_path_to_return
31
+ if params[:return_to]
32
+ params[:return_to]
33
+ else
34
+ parameters = {}
35
+ if params[:parent_controller]
36
+ parameters[:controller] = params[:parent_controller]
37
+ #parameters[:eid] = params[:parent_controller] # not neeeded anymore?
38
+ end
39
+ parameters.merge! nested.to_params if nested?
40
+ if params[:parent_sti]
41
+ parameters[:controller] = params[:parent_sti]
42
+ #parameters[:eid] = nil # not neeeded anymore?
43
+ end
44
+ parameters[:parent_column] = nil
45
+ parameters[:parent_id] = nil
46
+ parameters[:action] = "index"
47
+ parameters[:id] = nil
48
+ parameters[:associated_id] = nil
49
+ parameters[:utf8] = nil
50
+ params_for(parameters)
51
+ end
52
+ end
53
+
54
+ def nested_singular_association?
55
+ nested? && (nested.belongs_to? || nested.has_one?)
56
+ end
57
+
58
+ def render_parent?
59
+ nested_singular_association? || params[:parent_sti]
60
+ end
61
+
62
+ def render_parent_options
63
+ if nested_singular_association?
64
+ {:controller => nested.parent_scaffold.controller_path, :action => :row, :id => nested.parent_id}
65
+ elsif params[:parent_sti]
66
+ options = params_for(:controller => params[:parent_sti], :action => render_parent_action, :parent_sti => nil)
67
+ options.merge(:id => @record.id) if render_parent_action == :row
68
+ end
69
+ end
70
+
71
+ def render_parent_action
72
+ begin
73
+ @parent_action = :row
74
+ if params[:parent_sti]
75
+ parent_controller = "#{params[:parent_sti].to_s.camelize}Controller".constantize
76
+ @parent_action = :index if action_name == 'create' && parent_controller.active_scaffold_config.actions.include?(:create) && parent_controller.active_scaffold_config.create.refresh_list == true
77
+ @parent_action = :index if action_name == 'update' && parent_controller.active_scaffold_config.actions.include?(:update) && parent_controller.active_scaffold_config.update.refresh_list == true
78
+ @parent_action = :index if action_name == 'destroy' && parent_controller.active_scaffold_config.actions.include?(:delete) && parent_controller.active_scaffold_config.delete.refresh_list == true
79
+ end
80
+ rescue ActiveScaffold::ControllerNotFound
81
+ end if @parent_action.nil?
82
+ @parent_action
83
+ end
84
+
85
+ def build_associated(column, record)
86
+ if column.singular_association?
87
+ record.send(:"build_#{column.name}")
88
+ else
89
+ record.send(column.name).build
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end