wice_grid 3.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG +412 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +1179 -0
  5. data/Rakefile +42 -0
  6. data/SAVED_QUERIES_HOWTO.rdoc +123 -0
  7. data/VERSION +1 -0
  8. data/lib/generators/wice_grid/templates/calendarview.css +107 -0
  9. data/lib/generators/wice_grid/templates/calendarview.js +1168 -0
  10. data/lib/generators/wice_grid/templates/icons/arrow_down.gif +0 -0
  11. data/lib/generators/wice_grid/templates/icons/arrow_up.gif +0 -0
  12. data/lib/generators/wice_grid/templates/icons/calendar_view_month.png +0 -0
  13. data/lib/generators/wice_grid/templates/icons/delete.png +0 -0
  14. data/lib/generators/wice_grid/templates/icons/expand.png +0 -0
  15. data/lib/generators/wice_grid/templates/icons/page_white_excel.png +0 -0
  16. data/lib/generators/wice_grid/templates/icons/page_white_find.png +0 -0
  17. data/lib/generators/wice_grid/templates/icons/table.png +0 -0
  18. data/lib/generators/wice_grid/templates/icons/table_refresh.png +0 -0
  19. data/lib/generators/wice_grid/templates/icons/tick_all.png +0 -0
  20. data/lib/generators/wice_grid/templates/icons/untick_all.png +0 -0
  21. data/lib/generators/wice_grid/templates/wice_grid.css +173 -0
  22. data/lib/generators/wice_grid/templates/wice_grid.yml +279 -0
  23. data/lib/generators/wice_grid/templates/wice_grid_config.rb +154 -0
  24. data/lib/generators/wice_grid/templates/wice_grid_jquery.js +161 -0
  25. data/lib/generators/wice_grid/templates/wice_grid_prototype.js +153 -0
  26. data/lib/generators/wice_grid/wice_grid_assets_jquery_generator.rb +32 -0
  27. data/lib/generators/wice_grid/wice_grid_assets_prototype_generator.rb +34 -0
  28. data/lib/grid_output_buffer.rb +52 -0
  29. data/lib/grid_renderer.rb +535 -0
  30. data/lib/helpers/js_calendar_helpers.rb +183 -0
  31. data/lib/helpers/wice_grid_misc_view_helpers.rb +113 -0
  32. data/lib/helpers/wice_grid_serialized_queries_view_helpers.rb +91 -0
  33. data/lib/helpers/wice_grid_view_helpers.rb +781 -0
  34. data/lib/js_adaptors/jquery_adaptor.rb +145 -0
  35. data/lib/js_adaptors/js_adaptor.rb +12 -0
  36. data/lib/js_adaptors/prototype_adaptor.rb +168 -0
  37. data/lib/table_column_matrix.rb +51 -0
  38. data/lib/tasks/wice_grid_tasks.rake +28 -0
  39. data/lib/view_columns.rb +486 -0
  40. data/lib/views/create.rjs +13 -0
  41. data/lib/views/create_jq.rjs +31 -0
  42. data/lib/views/delete.rjs +12 -0
  43. data/lib/views/delete_jq.rjs +26 -0
  44. data/lib/wice_grid.rb +827 -0
  45. data/lib/wice_grid_controller.rb +165 -0
  46. data/lib/wice_grid_core_ext.rb +179 -0
  47. data/lib/wice_grid_misc.rb +98 -0
  48. data/lib/wice_grid_serialized_queries_controller.rb +86 -0
  49. data/lib/wice_grid_serialized_query.rb +15 -0
  50. data/lib/wice_grid_spreadsheet.rb +33 -0
  51. data/test/.gitignore +2 -0
  52. data/test/database.yml +21 -0
  53. data/test/schema.rb +33 -0
  54. data/test/test_helper.rb +89 -0
  55. data/test/views/projects_and_people_grid.html.erb +12 -0
  56. data/test/views/projects_and_people_grid_invalid.html.erb +12 -0
  57. data/test/views/simple_projects_grid.html.erb +9 -0
  58. data/test/wice_grid_core_ext_test.rb +183 -0
  59. data/test/wice_grid_functional_test.rb +68 -0
  60. data/test/wice_grid_misc_test.rb +41 -0
  61. data/test/wice_grid_test.rb +42 -0
  62. data/test/wice_grid_view_helper_test.rb +12 -0
  63. data/wice_grid.gemspec +111 -0
  64. metadata +153 -0
@@ -0,0 +1,13 @@
1
+ if @notification_messages
2
+ page.replace_html(@notification_messages_dom_id, %!<div class="wice_grid_notice"> #{h @notification_messages}</div>!)
3
+ end
4
+
5
+ if @error_messages
6
+ page.replace_html(@notification_messages_dom_id, %!<div class="errorExplanation"> #{h @error_messages} </div>!)
7
+ else
8
+ page.replace(@query_list_dom_id, saved_queries_list(@grid_name, @saved_query, controller.extra))
9
+ page[@query_list_dom_id].visual_effect :highlight
10
+ page.replace_html(@grid_title_id, content_tag(:h3,@saved_query.name))
11
+ end
12
+
13
+ page[@notification_messages_dom_id].show
@@ -0,0 +1,31 @@
1
+ if @notification_messages
2
+
3
+ notification_messages = ActiveSupport::JSON.encode(
4
+ %!<div class="wice_grid_notice"> #{h @notification_messages}</div>!
5
+ )
6
+
7
+ page << "$('##{@notification_messages_dom_id}').html(#{notification_messages});"
8
+ end
9
+
10
+ if @error_messages
11
+ error_messages = ActiveSupport::JSON.encode(
12
+ %!<div class="errorExplanation"> #{h @error_messages} </div>!
13
+ )
14
+
15
+ page << "$('##{@notification_messages_dom_id}').html(#{error_messages});"
16
+ else
17
+ new_list = ActiveSupport::JSON.encode(
18
+ saved_queries_list(@grid_name, @saved_query, controller.extra)
19
+ )
20
+ page << "$('##{@query_list_dom_id}').replaceWith(#{new_list});"
21
+
22
+ page << "$('##{@query_list_dom_id}').effect('highlight');"
23
+
24
+ new_grid_name = ActiveSupport::JSON.encode(
25
+ content_tag(:h3,@saved_query.name)
26
+ )
27
+
28
+ page << "$('##{@grid_title_id}').html(#{new_grid_name});"
29
+ end
30
+
31
+ page << %!$("##{@notification_messages_dom_id}").show();!
@@ -0,0 +1,12 @@
1
+ if @notification_messages
2
+ page.replace_html(@notification_messages_dom_id, %!<div class="wice_grid_notice"> #{h @notification_messages}</div>!)
3
+ end
4
+
5
+ if @error_messages
6
+ page.replace_html(@notification_messages_dom_id, %!<div class="errorExplanation"> #{h @error_messages} </div>!)
7
+ else
8
+ page.replace(@query_list_dom_id, saved_queries_list(@grid_name, @current, controller.extra))
9
+ page[@query_list_dom_id].visual_effect :highlight
10
+ end
11
+
12
+ page[@notification_messages_dom_id].show
@@ -0,0 +1,26 @@
1
+ if @notification_messages
2
+ notification_messages = ActiveSupport::JSON.encode(
3
+ %!<div class="wice_grid_notice"> #{h @notification_messages}</div>!
4
+ )
5
+
6
+ page << "$('##{@notification_messages_dom_id}').html(#{notification_messages});"
7
+ end
8
+
9
+
10
+
11
+ if @error_messages
12
+ error_messages = ActiveSupport::JSON.encode(
13
+ %!<div class="errorExplanation"> #{h @error_messages} </div>!
14
+ )
15
+
16
+ page << "$('##{@notification_messages_dom_id}').html(#{error_messages});"
17
+ else
18
+ new_list = ActiveSupport::JSON.encode(
19
+ saved_queries_list(@grid_name, @saved_query, controller.extra)
20
+ )
21
+ page << "$('##{@query_list_dom_id}').replaceWith(#{new_list});"
22
+
23
+ page << "$('##{@query_list_dom_id}').effect('highlight');"
24
+ end
25
+
26
+ page << %!$("##{@notification_messages_dom_id}").show();!
data/lib/wice_grid.rb ADDED
@@ -0,0 +1,827 @@
1
+ # encoding: UTF-8
2
+ require 'will_paginate.rb'
3
+ require 'wice_grid_misc.rb'
4
+ require 'wice_grid_core_ext.rb'
5
+ require 'grid_renderer.rb'
6
+ require 'table_column_matrix.rb'
7
+ require 'helpers/wice_grid_view_helpers.rb'
8
+ require 'helpers/wice_grid_misc_view_helpers.rb'
9
+ require 'helpers/wice_grid_serialized_queries_view_helpers.rb'
10
+ require 'helpers/wice_grid_view_helpers.rb'
11
+ require 'helpers/js_calendar_helpers.rb'
12
+ require 'grid_output_buffer.rb'
13
+ require 'wice_grid_controller.rb'
14
+ require 'wice_grid_spreadsheet.rb'
15
+ require 'wice_grid_serialized_queries_controller.rb'
16
+ require 'js_adaptors/js_adaptor.rb'
17
+ require 'js_adaptors/jquery_adaptor.rb'
18
+ require 'js_adaptors/prototype_adaptor.rb'
19
+ require 'view_columns.rb'
20
+
21
+
22
+
23
+ ActionController::Base.send(:helper_method, :wice_grid_custom_filter_params)
24
+
25
+ module Wice
26
+
27
+ class WiceGridRailtie < Rails::Railtie #:nodoc:
28
+
29
+ initializer "wice_grid_railtie.configure_rails_initialization" do |app|
30
+
31
+ ActionController::Base.send(:include, Wice::Controller)
32
+
33
+ ActiveRecord::ConnectionAdapters::Column.send(:include, ::Wice::WiceGridExtentionToActiveRecordColumn)
34
+
35
+ Wice::GridRenderer.send(:include, ::WillPaginate::ViewHelpers)
36
+
37
+ ::ActionView::Base.class_eval { include Wice::GridViewHelper }
38
+
39
+ ActiveRecord::Base.send(:include, ::Wice::MergeConditions)
40
+
41
+ [ActionView::Helpers::AssetTagHelper,
42
+ ActionView::Helpers::TagHelper,
43
+ ActionView::Helpers::JavaScriptHelper,
44
+ ActionView::Helpers::FormTagHelper].each do |m|
45
+ JsCalendarHelpers.send(:include, m)
46
+ end
47
+
48
+ require 'wice_grid_serialized_query.rb'
49
+ end
50
+
51
+ rake_tasks do
52
+ load 'tasks/wice_grid_tasks.rake'
53
+ end
54
+ end
55
+
56
+ class WiceGrid
57
+
58
+ attr_reader :klass, :name, :resultset, :custom_order, :query_store_model
59
+ attr_reader :ar_options, :status, :export_to_csv_enabled, :csv_file_name, :saved_query
60
+ attr_writer :renderer
61
+ attr_accessor :output_buffer, :view_helper_finished, :csv_tempfile
62
+
63
+ # core workflow methods START
64
+
65
+ def initialize(klass, controller, opts = {}) #:nodoc:
66
+ @controller = controller
67
+
68
+
69
+ unless klass.kind_of?(Class) && klass.ancestors.index(ActiveRecord::Base)
70
+ raise WiceGridArgumentError.new("ActiveRecord model class (second argument) must be a Class derived from ActiveRecord::Base")
71
+ end
72
+
73
+ # validate :with_resultset & :with_paginated_resultset
74
+ [:with_resultset, :with_paginated_resultset].each do |callback_symbol|
75
+ unless [NilClass, Symbol, Proc].index(opts[callback_symbol].class)
76
+ raise WiceGridArgumentError.new(":#{callback_symbol} must be either a Proc or Symbol object")
77
+ end
78
+ end
79
+
80
+ opts[:order_direction].downcase! if opts[:order_direction].kind_of?(String)
81
+
82
+ # validate :order_direction
83
+ if opts[:order_direction] && ! (opts[:order_direction] == 'asc' ||
84
+ opts[:order_direction] == :asc ||
85
+ opts[:order_direction] == 'desc' ||
86
+ opts[:order_direction] == :desc)
87
+ raise WiceGridArgumentError.new(":order_direction must be either 'asc' or 'desc'.")
88
+ end
89
+
90
+ # options that are understood
91
+ @options = {
92
+ :conditions => nil,
93
+ :csv_file_name => nil,
94
+ :custom_order => {},
95
+ :enable_export_to_csv => Defaults::ENABLE_EXPORT_TO_CSV,
96
+ :group => nil,
97
+ :include => nil,
98
+ :joins => nil,
99
+ :name => Defaults::GRID_NAME,
100
+ :order => nil,
101
+ :order_direction => Defaults::ORDER_DIRECTION,
102
+ :page => 1,
103
+ :per_page => Defaults::PER_PAGE,
104
+ :saved_query => nil,
105
+ :select => nil,
106
+ :total_entries => nil,
107
+ :with_paginated_resultset => nil,
108
+ :with_resultset => nil
109
+ }
110
+
111
+ # validate parameters
112
+ opts.assert_valid_keys(@options.keys)
113
+
114
+ @options.merge!(opts)
115
+ @export_to_csv_enabled = @options[:enable_export_to_csv]
116
+ @csv_file_name = @options[:csv_file_name]
117
+
118
+ case @name = @options[:name]
119
+ when String
120
+ when Symbol
121
+ @name = @name.to_s
122
+ else
123
+ raise WiceGridArgumentError.new("name of the grid should be a string or a symbol")
124
+ end
125
+ raise WiceGridArgumentError.new("name of the grid can only contain alphanumeruc characters") unless @name =~ /^[a-zA-Z\d_]*$/
126
+
127
+ @klass = klass
128
+
129
+ @table_column_matrix = TableColumnMatrix.new
130
+ @table_column_matrix.default_model_class = @klass
131
+
132
+ @ar_options = {}
133
+ @status = HashWithIndifferentAccess.new
134
+
135
+ if @options[:order]
136
+ @options[:order] = @options[:order].to_s
137
+ @options[:order_direction] = @options[:order_direction].to_s
138
+
139
+ @status[:order_direction] = @options[:order_direction]
140
+ @status[:order] = @options[:order]
141
+
142
+ end
143
+ @status[:total_entries] = @options[:total_entries]
144
+ @status[:per_page] = @options[:per_page]
145
+ @status[:page] = @options[:page]
146
+ @status[:conditions] = @options[:conditions]
147
+ @status[:f] = @options[:f]
148
+
149
+ process_loading_query
150
+ process_params
151
+
152
+ @ar_options_formed = false
153
+
154
+ @method_scoping = @klass.send(:scoped_methods)[-1]
155
+ end
156
+
157
+ # A block executed from within the plugin to process records of the current page.
158
+ # The argument to the callback is the array of the records. See the README for more details.
159
+ def with_paginated_resultset(&callback)
160
+ @options[:with_paginated_resultset] = callback
161
+ end
162
+
163
+ # A block executed from within the plugin to process all records browsable through
164
+ # all pages with the current filters. The argument to
165
+ # the callback is a lambda object which returns the list of records when called. See the README for the explanation.
166
+ def with_resultset(&callback)
167
+ @options[:with_resultset] = callback
168
+ end
169
+
170
+ def process_loading_query #:nodoc:
171
+ @saved_query = nil
172
+ if params[name] && params[name][:q]
173
+ @saved_query = load_query(params[name][:q])
174
+ params[name].delete(:q)
175
+ elsif @options[:saved_query]
176
+ if @options[:saved_query].is_a? ActiveRecord::Base
177
+ @saved_query = @options[:saved_query]
178
+ else
179
+ @saved_query = load_query(@options[:saved_query])
180
+ end
181
+ else
182
+ return
183
+ end
184
+
185
+ unless @saved_query.nil?
186
+ params[name] = HashWithIndifferentAccess.new if params[name].blank?
187
+ [:f, :order, :order_direction].each do |key|
188
+ if @saved_query.query[key].blank?
189
+ params[name].delete(key)
190
+ else
191
+ params[name][key] = @saved_query.query[key]
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ def process_params #:nodoc:
198
+ if this_grid_params
199
+ @status.merge!(this_grid_params)
200
+ @status.delete(:export) unless self.export_to_csv_enabled
201
+ end
202
+ end
203
+
204
+ def declare_column(column_name, model_class, custom_filter_active, table_alias) #:nodoc:
205
+ if model_class # this is an included table
206
+ column = @table_column_matrix.get_column_by_model_class_and_column_name(model_class, column_name)
207
+ raise WiceGridArgumentError.new("Column '#{column_name}' is not found in table '#{model_class.table_name}'!") if column.nil?
208
+ main_table = false
209
+ table_name = model_class.table_name
210
+ else
211
+ column = @table_column_matrix.get_column_in_default_model_class_by_column_name(column_name)
212
+ if column.nil?
213
+ raise WiceGridArgumentError.new("Column '#{column_name}' is not found in table '#{@klass.table_name}'! " +
214
+ "If '#{column_name}' belongs to another table you should declare it in :include or :join when initialising " +
215
+ "the grid, and specify :model_class in column declaration.")
216
+ end
217
+ main_table = true
218
+ table_name = @table_column_matrix.default_model_class.table_name
219
+ end
220
+
221
+ if column
222
+ conditions, current_parameter_name = column.wg_initialize_request_parameters(@status[:f], main_table, table_alias, custom_filter_active)
223
+ if @status[:f] && conditions.blank?
224
+ @status[:f].delete(current_parameter_name)
225
+ end
226
+
227
+ @table_column_matrix.add_condition(column, conditions)
228
+ [column, table_name , main_table]
229
+ else
230
+ nil
231
+ end
232
+ end
233
+
234
+ def form_ar_options(opts = {}) #:nodoc:
235
+
236
+ return if @ar_options_formed
237
+ @ar_options_formed = true unless opts[:forget_generated_options]
238
+
239
+ # validate @status[:order_direction]
240
+ @status[:order_direction] = case @status[:order_direction]
241
+ when /desc/i
242
+ 'desc'
243
+ when /asc/i
244
+ 'asc'
245
+ else
246
+ ''
247
+ end
248
+
249
+ # conditions
250
+ if @table_column_matrix.generated_conditions.size == 0
251
+ @status.delete(:f)
252
+ end
253
+
254
+ @ar_options[:conditions] = klass.send(:merge_conditions, @status[:conditions], * @table_column_matrix.conditions )
255
+ # conditions processed
256
+
257
+ if (! opts[:skip_ordering]) && @status[:order]
258
+ @ar_options[:order] = add_custom_order_sql(complete_column_name(@status[:order]))
259
+
260
+ @ar_options[:order] += ' ' + @status[:order_direction]
261
+ end
262
+
263
+ if self.output_html?
264
+ @ar_options[:per_page] = if all_record_mode?
265
+ # reset the :pp value in all records mode
266
+ @status[:pp] = count_resultset_without_paging_without_user_filters
267
+ else
268
+ @status[:per_page]
269
+ end
270
+
271
+ @ar_options[:page] = @status[:page]
272
+ @ar_options[:total_entries] = @status[:total_entries] if @status[:total_entries]
273
+ end
274
+
275
+ @ar_options[:joins] = @options[:joins]
276
+ @ar_options[:include] = @options[:include]
277
+ @ar_options[:group] = @options[:group]
278
+ @ar_options[:select] = @options[:select]
279
+ end
280
+
281
+ def read #:nodoc:
282
+ form_ar_options
283
+ with_exclusive_scope do
284
+ @resultset = self.output_csv? ? @klass.find(:all, @ar_options) : @klass.paginate(@ar_options)
285
+ end
286
+ invoke_resultset_callbacks
287
+ end
288
+
289
+
290
+ # core workflow methods END
291
+
292
+ # Getters
293
+
294
+ def filter_params(view_column) #:nodoc:
295
+ column_name = view_column.attribute_name_fully_qualified_for_all_but_main_table_columns
296
+ if @status[:f] && @status[:f][column_name]
297
+ @status[:f][column_name]
298
+ else
299
+ {}
300
+ end
301
+ end
302
+
303
+ def resultset #:nodoc:
304
+ self.read unless @resultset # database querying is late!
305
+ @resultset
306
+ end
307
+
308
+ def each #:nodoc:
309
+ self.read unless @resultset # database querying is late!
310
+ @resultset.each do |r|
311
+ yield r
312
+ end
313
+ end
314
+
315
+ def ordered_by?(column) #:nodoc:
316
+ return nil if @status[:order].blank?
317
+ if column.main_table && ! offs = @status[:order].index('.')
318
+ @status[:order] == column.attribute_name
319
+ else
320
+ @status[:order] == column.table_alias_or_table_name + '.' + column.attribute_name
321
+ end
322
+ end
323
+
324
+ def ordered_by #:nodoc:
325
+ @status[:order]
326
+ end
327
+
328
+
329
+ def order_direction #:nodoc:
330
+ @status[:order_direction]
331
+ end
332
+
333
+ def filtering_on? #:nodoc:
334
+ not @status[:f].blank?
335
+ end
336
+
337
+ def filtered_by #:nodoc:
338
+ @status[:f].nil? ? [] : @status[:f].keys
339
+ end
340
+
341
+ def filtered_by?(view_column) #:nodoc:
342
+ @status[:f].nil? ? false : @status[:f].has_key?(view_column.attribute_name_fully_qualified_for_all_but_main_table_columns)
343
+ end
344
+
345
+ def get_state_as_parameter_value_pairs(including_saved_query_request = false) #:nodoc:
346
+ res = []
347
+ unless status[:f].blank?
348
+ status[:f].parameter_names_and_values([name, 'f']).collect do |param_name, value|
349
+ if value.is_a?(Array)
350
+ param_name_ar = param_name + '[]'
351
+ value.each do |v|
352
+ res << [param_name_ar, v]
353
+ end
354
+ else
355
+ res << [param_name, value]
356
+ end
357
+ end
358
+ end
359
+
360
+ if including_saved_query_request && @saved_query
361
+ res << ["#{name}[q]", @saved_query.id ]
362
+ end
363
+
364
+ [:order, :order_direction].select{|parameter|
365
+ status[parameter]
366
+ }.collect do |parameter|
367
+ res << ["#{name}[#{parameter}]", status[parameter] ]
368
+ end
369
+
370
+ res
371
+ end
372
+
373
+ def count #:nodoc:
374
+ form_ar_options(:skip_ordering => true, :forget_generated_options => true)
375
+ @klass.count(:conditions => @ar_options[:conditions], :joins => @ar_options[:joins], :include => @ar_options[:include], :group => @ar_options[:group])
376
+ end
377
+
378
+ alias_method :size, :count
379
+
380
+ def empty? #:nodoc:
381
+ self.count == 0
382
+ end
383
+
384
+ # with this variant we get even those values which do not appear in the resultset
385
+ def distinct_values_for_column(column) #:nodoc:
386
+ res = column.model_klass.find(:all, :select => "distinct #{column.name}", :order => "#{column.name} asc").collect{|ar|
387
+ ar[column.name]
388
+ }.reject{|e| e.blank?}.map{|i|[i,i]}
389
+ end
390
+
391
+
392
+ def distinct_values_for_column_in_resultset(messages) #:nodoc:
393
+ uniq_vals = Set.new
394
+
395
+ resultset_without_paging_without_user_filters.each do |ar|
396
+ v = ar.deep_send(*messages)
397
+ uniq_vals << v unless v.nil?
398
+ end
399
+ return uniq_vals.to_a.map{|i|
400
+ if i.is_a?(Array) && i.size == 2
401
+ i
402
+ elsif i.is_a?(Hash) && i.size == 1
403
+ i.to_a.flatten
404
+ else
405
+ [i,i]
406
+ end
407
+ }.sort{|a,b| a[0]<=>b[0]}
408
+ end
409
+
410
+ def output_csv? #:nodoc:
411
+ @status[:export] == 'csv'
412
+ end
413
+
414
+ def output_html? #:nodoc:
415
+ @status[:export].blank?
416
+ end
417
+
418
+ def all_record_mode? #:nodoc:
419
+ @status[:pp]
420
+ end
421
+
422
+ def dump_status #:nodoc:
423
+ " params: #{params[name].inspect}\n" +
424
+ " status: #{@status.inspect}\n" +
425
+ " ar_options #{@ar_options.inspect}\n"
426
+ end
427
+
428
+
429
+ def selected_records #:nodoc:
430
+ STDERR.puts "WiceGrid: Parameter :#{selected_records} is deprecated, use :#{all_pages_records} or :#{current_page_records} instead!"
431
+ all_pages_records
432
+ end
433
+
434
+ # Returns the list of objects browsable through all pages with the current filters.
435
+ # Should only be called after the +grid+ helper.
436
+ def all_pages_records
437
+ raise WiceGridException.new("all_pages_records can only be called only after the grid view helper") unless self.view_helper_finished
438
+ resultset_without_paging_with_user_filters
439
+ end
440
+
441
+ # Returns the list of objects displayed on current page. Should only be called after the +grid+ helper.
442
+ def current_page_records
443
+ raise WiceGridException.new("current_page_records can only be called only after the grid view helper") unless self.view_helper_finished
444
+ @resultset
445
+ end
446
+
447
+
448
+
449
+ protected
450
+
451
+ def invoke_resultset_callback(callback, argument) #:nodoc:
452
+ case callback
453
+ when Proc
454
+ callback.call(argument)
455
+ when Symbol
456
+ @controller.send(callback, argument)
457
+ end
458
+ end
459
+
460
+ def invoke_resultset_callbacks #:nodoc:
461
+ invoke_resultset_callback(@options[:with_paginated_resultset], @resultset)
462
+ invoke_resultset_callback(@options[:with_resultset], lambda{self.send(:resultset_without_paging_with_user_filters)})
463
+ end
464
+
465
+ def with_exclusive_scope #:nodoc:
466
+ if @method_scoping
467
+ @klass.send(:with_exclusive_scope, @method_scoping) do
468
+ yield
469
+ end
470
+ else
471
+ yield
472
+ end
473
+ end
474
+
475
+
476
+ def add_custom_order_sql(fully_qualified_column_name) #:nodoc:
477
+ custom_order = if @options[:custom_order].has_key?(fully_qualified_column_name)
478
+ @options[:custom_order][fully_qualified_column_name]
479
+ else
480
+ if view_column = @renderer[fully_qualified_column_name]
481
+ view_column.custom_order
482
+ else
483
+ nil
484
+ end
485
+ end
486
+
487
+ if custom_order.blank?
488
+ ActiveRecord::Base.connection.quote_table_name(fully_qualified_column_name.strip)
489
+ else
490
+ if custom_order.is_a? String
491
+ custom_order.gsub(/\?/, fully_qualified_column_name)
492
+ elsif custom_order.is_a? Proc
493
+ custom_order.call(fully_qualified_column_name)
494
+ else
495
+ raise WiceGridArgumentError.new("invalid custom order #{custom_order.inspect}")
496
+ end
497
+ end
498
+ end
499
+
500
+ def complete_column_name(col_name) #:nodoc:
501
+ if col_name.index('.') # already has a table name
502
+ col_name
503
+ else # add the default table
504
+ "#{@klass.table_name}.#{col_name}"
505
+ end
506
+ end
507
+
508
+ def params #:nodoc:
509
+ @controller.params
510
+ end
511
+
512
+ def this_grid_params #:nodoc:
513
+ params[name]
514
+ end
515
+
516
+
517
+ def resultset_without_paging_without_user_filters #:nodoc:
518
+ form_ar_options
519
+ with_exclusive_scope do
520
+ @klass.find(:all, :joins => @ar_options[:joins],
521
+ :include => @ar_options[:include],
522
+ :group => @ar_options[:group],
523
+ :conditions => @options[:conditions])
524
+ end
525
+ end
526
+
527
+ def count_resultset_without_paging_without_user_filters #:nodoc:
528
+ form_ar_options
529
+ with_exclusive_scope do
530
+ @klass.count(
531
+ :joins => @ar_options[:joins],
532
+ :include => @ar_options[:include],
533
+ :group => @ar_options[:group],
534
+ :conditions => @options[:conditions]
535
+ )
536
+ end
537
+ end
538
+
539
+
540
+ def resultset_without_paging_with_user_filters #:nodoc:
541
+ form_ar_options
542
+ with_exclusive_scope do
543
+ @klass.find(:all, :joins => @ar_options[:joins],
544
+ :include => @ar_options[:include],
545
+ :group => @ar_options[:group],
546
+ :conditions => @ar_options[:conditions],
547
+ :order => @ar_options[:order])
548
+ end
549
+ end
550
+
551
+
552
+ def load_query(query_id) #:nodoc:
553
+ @query_store_model ||= Wice::get_query_store_model
554
+ query = @query_store_model.find_by_id_and_grid_name(query_id, self.name)
555
+ Wice::log("Query with id #{query_id} for grid '#{self.name}' not found!!!") if query.nil?
556
+ query
557
+ end
558
+
559
+
560
+ end
561
+
562
+ # routines called from WiceGridExtentionToActiveRecordColumn (ActiveRecord::ConnectionAdapters::Column) or FilterConditionsGenerator classes
563
+ module GridTools #:nodoc:
564
+ class << self
565
+ def special_value(str) #:nodoc:
566
+ str =~ /^\s*(not\s+)?null\s*$/i
567
+ end
568
+
569
+ # create a Time instance out of parameters
570
+ def params_2_datetime(par) #:nodoc:
571
+ return nil if par.blank?
572
+ params = [par[:year], par[:month], par[:day], par[:hour], par[:minute]].collect{|v| v.blank? ? nil : v.to_i}
573
+ begin
574
+ Time.local(*params)
575
+ rescue ArgumentError, TypeError
576
+ nil
577
+ end
578
+ end
579
+
580
+ # create a Date instance out of parameters
581
+ def params_2_date(par) #:nodoc:
582
+ return nil if par.blank?
583
+ params = [par[:year], par[:month], par[:day]].collect{|v| v.blank? ? nil : v.to_i}
584
+ begin
585
+ Date.civil(*params)
586
+ rescue ArgumentError, TypeError
587
+ nil
588
+ end
589
+ end
590
+
591
+ end
592
+ end
593
+
594
+ # to be mixed in into ActiveRecord::ConnectionAdapters::Column
595
+ module WiceGridExtentionToActiveRecordColumn #:nodoc:
596
+
597
+ attr_accessor :model_klass
598
+
599
+ def alias_or_table_name(table_alias)
600
+ table_alias || self.model_klass.table_name
601
+ end
602
+
603
+ def wg_initialize_request_parameters(all_filter_params, main_table, table_alias, custom_filter_active) #:nodoc:
604
+ @request_params = nil
605
+ return if all_filter_params.nil?
606
+
607
+ # if the parameter does not specify the table name we only allow columns in the default table to use these parameters
608
+ if main_table && @request_params = all_filter_params[self.name]
609
+ current_parameter_name = self.name
610
+ elsif @request_params = all_filter_params[alias_or_table_name(table_alias) + '.' + self.name]
611
+ current_parameter_name = alias_or_table_name(table_alias) + '.' + self.name
612
+ end
613
+
614
+ # Preprocess incoming parameters for datetime, if what's coming in is
615
+ # a datetime (with custom_filter it can be anything else, and not
616
+ # the datetime hash {:fr => ..., :to => ...})
617
+ if @request_params
618
+ if (self.type == :datetime || self.type == :timestamp) && @request_params.is_a?(Hash)
619
+ [:fr, :to].each do |sym|
620
+ if @request_params[sym]
621
+ if @request_params[sym].is_a?(String)
622
+ @request_params[sym] = Wice::Defaults::DATETIME_PARSER.call(@request_params[sym])
623
+ elsif @request_params[sym].is_a?(Hash)
624
+ @request_params[sym] = ::Wice::GridTools.params_2_datetime(@request_params[sym])
625
+ end
626
+ end
627
+ end
628
+
629
+ end
630
+
631
+ # Preprocess incoming parameters for date, if what's coming in is
632
+ # a date (with custom_filter it can be anything else, and not
633
+ # the date hash {:fr => ..., :to => ...})
634
+ if self.type == :date && @request_params.is_a?(Hash)
635
+ [:fr, :to].each do |sym|
636
+ if @request_params[sym]
637
+ if @request_params[sym].is_a?(String)
638
+ @request_params[sym] = Wice::Defaults::DATE_PARSER.call(@request_params[sym])
639
+ elsif @request_params[sym].is_a?(Hash)
640
+ @request_params[sym] = ::Wice::GridTools.params_2_date(@request_params[sym])
641
+ end
642
+ end
643
+ end
644
+ end
645
+ end
646
+
647
+ return wg_generate_conditions(table_alias, custom_filter_active), current_parameter_name
648
+ end
649
+
650
+ def wg_generate_conditions(table_alias, custom_filter_active) #:nodoc:
651
+ return nil if @request_params.nil?
652
+
653
+ if custom_filter_active
654
+ return ::Wice::FilterConditionsGeneratorCustomFilter.new(self).generate_conditions(table_alias, @request_params)
655
+ end
656
+
657
+ column_type = self.type.to_s
658
+
659
+ processor_class = ::Wice::FilterConditionsGenerator.handled_type[column_type]
660
+
661
+ if processor_class
662
+ return processor_class.new(self).generate_conditions(table_alias, @request_params)
663
+ else
664
+ Wice.log("No processor for database type #{column_type}!!!")
665
+ nil
666
+ end
667
+ end
668
+
669
+ end
670
+
671
+ class FilterConditionsGenerator #:nodoc:
672
+
673
+ cattr_accessor :handled_type
674
+ @@handled_type = HashWithIndifferentAccess.new
675
+
676
+ def initialize(column) #:nodoc:
677
+ @column = column
678
+ end
679
+ end
680
+
681
+ class FilterConditionsGeneratorCustomFilter < FilterConditionsGenerator #:nodoc:
682
+
683
+ def generate_conditions(table_alias, opts) #:nodoc:
684
+ if opts.empty?
685
+ Wice.log "empty parameters for the grid custom filter"
686
+ return false
687
+ end
688
+ opts = (opts.kind_of?(Array) && opts.size == 1) ? opts[0] : opts
689
+
690
+ if opts.kind_of?(Array)
691
+ opts_with_special_values, normal_opts = opts.partition{|v| ::Wice::GridTools.special_value(v)}
692
+
693
+ conditions_ar = if normal_opts.size > 0
694
+ [" #{@column.alias_or_table_name(table_alias)}.#{@column.name} IN ( " + (['?'] * normal_opts.size).join(', ') + ' )'] + normal_opts
695
+ else
696
+ []
697
+ end
698
+
699
+ if opts_with_special_values.size > 0
700
+ special_conditions = opts_with_special_values.collect{|v| " #{@column.alias_or_table_name(table_alias)}.#{@column.name} is " + v}.join(' or ')
701
+ if conditions_ar.size > 0
702
+ conditions_ar[0] = " (#{conditions_ar[0]} or #{special_conditions} ) "
703
+ else
704
+ conditions_ar = " ( #{special_conditions} ) "
705
+ end
706
+ end
707
+ conditions_ar
708
+ else
709
+ if ::Wice::GridTools.special_value(opts)
710
+ " #{@column.alias_or_table_name(table_alias)}.#{@column.name} is " + opts
711
+ else
712
+ [" #{@column.alias_or_table_name(table_alias)}.#{@column.name} = ?", opts]
713
+ end
714
+ end
715
+ end
716
+
717
+ end
718
+
719
+ class FilterConditionsGeneratorBoolean < FilterConditionsGenerator #:nodoc:
720
+ @@handled_type[:boolean] = self
721
+
722
+ def generate_conditions(table_alias, opts) #:nodoc:
723
+ unless (opts.kind_of?(Array) && opts.size == 1)
724
+ Wice.log "invalid parameters for the grid boolean filter - must be an one item array: #{opts.inspect}"
725
+ return false
726
+ end
727
+ opts = opts[0]
728
+ if opts == 'f'
729
+ [" (#{@column.alias_or_table_name(table_alias)}.#{@column.name} = ? or #{@column.alias_or_table_name(table_alias)}.#{@column.name} is null) ", false]
730
+ elsif opts == 't'
731
+ [" #{@column.alias_or_table_name(table_alias)}.#{@column.name} = ?", true]
732
+ else
733
+ nil
734
+ end
735
+ end
736
+ end
737
+
738
+ class FilterConditionsGeneratorString < FilterConditionsGenerator #:nodoc:
739
+ @@handled_type[:string] = self
740
+ @@handled_type[:text] = self
741
+
742
+ def generate_conditions(table_alias, opts) #:nodoc:
743
+ if opts.kind_of? String
744
+ string_fragment = opts
745
+ negation = ''
746
+ elsif (opts.kind_of? Hash) && opts.has_key?(:v)
747
+ string_fragment = opts[:v]
748
+ negation = opts[:n] == '1' ? 'NOT' : ''
749
+ else
750
+ Wice.log "invalid parameters for the grid string filter - must be a string: #{opts.inspect} or a Hash with keys :v and :n"
751
+ return false
752
+ end
753
+ if string_fragment.empty?
754
+ Wice.log "invalid parameters for the grid string filter - empty string"
755
+ return false
756
+ end
757
+ [" #{negation} #{@column.alias_or_table_name(table_alias)}.#{@column.name} #{::Wice.get_string_matching_operators(@column.model_klass)} ?",
758
+ '%' + string_fragment + '%']
759
+ end
760
+
761
+ end
762
+
763
+ class FilterConditionsGeneratorInteger < FilterConditionsGenerator #:nodoc:
764
+ @@handled_type[:integer] = self
765
+ @@handled_type[:float] = self
766
+ @@handled_type[:decimal] = self
767
+
768
+ def generate_conditions(table_alias, opts) #:nodoc:
769
+ unless opts.kind_of? Hash
770
+ Wice.log "invalid parameters for the grid integer filter - must be a hash"
771
+ return false
772
+ end
773
+ conditions = [[]]
774
+ if opts[:fr]
775
+ if opts[:fr] =~ /\d/
776
+ conditions[0] << " #{@column.alias_or_table_name(table_alias)}.#{@column.name} >= ? "
777
+ conditions << opts[:fr]
778
+ else
779
+ opts.delete(:fr)
780
+ end
781
+ end
782
+
783
+ if opts[:to]
784
+ if opts[:to] =~ /\d/
785
+ conditions[0] << " #{@column.alias_or_table_name(table_alias)}.#{@column.name} <= ? "
786
+ conditions << opts[:to]
787
+ else
788
+ opts.delete(:to)
789
+ end
790
+ end
791
+
792
+ if conditions.size == 1
793
+ Wice.log "invalid parameters for the grid integer filter - either range limits are not supplied or they are not numeric"
794
+ return false
795
+ end
796
+
797
+ conditions[0] = conditions[0].join(' and ')
798
+
799
+ return conditions
800
+ end
801
+ end
802
+
803
+ class FilterConditionsGeneratorDate < FilterConditionsGenerator #:nodoc:
804
+ @@handled_type[:date] = self
805
+ @@handled_type[:datetime] = self
806
+ @@handled_type[:timestamp] = self
807
+
808
+ def generate_conditions(table_alias, opts) #:nodoc:
809
+ conditions = [[]]
810
+ if opts[:fr]
811
+ conditions[0] << " #{@column.alias_or_table_name(table_alias)}.#{@column.name} >= ? "
812
+ conditions << opts[:fr]
813
+ end
814
+
815
+ if opts[:to]
816
+ conditions[0] << " #{@column.alias_or_table_name(table_alias)}.#{@column.name} <= ? "
817
+ conditions << opts[:to]
818
+ end
819
+
820
+ return false if conditions.size == 1
821
+
822
+ conditions[0] = conditions[0].join(' and ')
823
+ return conditions
824
+ end
825
+ end
826
+
827
+ end