wice_grid 3.0.0.pre1

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 (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