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.
- data/.gitignore +1 -0
- data/CHANGELOG +412 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +1179 -0
- data/Rakefile +42 -0
- data/SAVED_QUERIES_HOWTO.rdoc +123 -0
- data/VERSION +1 -0
- data/lib/generators/wice_grid/templates/calendarview.css +107 -0
- data/lib/generators/wice_grid/templates/calendarview.js +1168 -0
- data/lib/generators/wice_grid/templates/icons/arrow_down.gif +0 -0
- data/lib/generators/wice_grid/templates/icons/arrow_up.gif +0 -0
- data/lib/generators/wice_grid/templates/icons/calendar_view_month.png +0 -0
- data/lib/generators/wice_grid/templates/icons/delete.png +0 -0
- data/lib/generators/wice_grid/templates/icons/expand.png +0 -0
- data/lib/generators/wice_grid/templates/icons/page_white_excel.png +0 -0
- data/lib/generators/wice_grid/templates/icons/page_white_find.png +0 -0
- data/lib/generators/wice_grid/templates/icons/table.png +0 -0
- data/lib/generators/wice_grid/templates/icons/table_refresh.png +0 -0
- data/lib/generators/wice_grid/templates/icons/tick_all.png +0 -0
- data/lib/generators/wice_grid/templates/icons/untick_all.png +0 -0
- data/lib/generators/wice_grid/templates/wice_grid.css +173 -0
- data/lib/generators/wice_grid/templates/wice_grid.yml +279 -0
- data/lib/generators/wice_grid/templates/wice_grid_config.rb +154 -0
- data/lib/generators/wice_grid/templates/wice_grid_jquery.js +161 -0
- data/lib/generators/wice_grid/templates/wice_grid_prototype.js +153 -0
- data/lib/generators/wice_grid/wice_grid_assets_jquery_generator.rb +32 -0
- data/lib/generators/wice_grid/wice_grid_assets_prototype_generator.rb +34 -0
- data/lib/grid_output_buffer.rb +52 -0
- data/lib/grid_renderer.rb +535 -0
- data/lib/helpers/js_calendar_helpers.rb +183 -0
- data/lib/helpers/wice_grid_misc_view_helpers.rb +113 -0
- data/lib/helpers/wice_grid_serialized_queries_view_helpers.rb +91 -0
- data/lib/helpers/wice_grid_view_helpers.rb +781 -0
- data/lib/js_adaptors/jquery_adaptor.rb +145 -0
- data/lib/js_adaptors/js_adaptor.rb +12 -0
- data/lib/js_adaptors/prototype_adaptor.rb +168 -0
- data/lib/table_column_matrix.rb +51 -0
- data/lib/tasks/wice_grid_tasks.rake +28 -0
- data/lib/view_columns.rb +486 -0
- data/lib/views/create.rjs +13 -0
- data/lib/views/create_jq.rjs +31 -0
- data/lib/views/delete.rjs +12 -0
- data/lib/views/delete_jq.rjs +26 -0
- data/lib/wice_grid.rb +827 -0
- data/lib/wice_grid_controller.rb +165 -0
- data/lib/wice_grid_core_ext.rb +179 -0
- data/lib/wice_grid_misc.rb +98 -0
- data/lib/wice_grid_serialized_queries_controller.rb +86 -0
- data/lib/wice_grid_serialized_query.rb +15 -0
- data/lib/wice_grid_spreadsheet.rb +33 -0
- data/test/.gitignore +2 -0
- data/test/database.yml +21 -0
- data/test/schema.rb +33 -0
- data/test/test_helper.rb +89 -0
- data/test/views/projects_and_people_grid.html.erb +12 -0
- data/test/views/projects_and_people_grid_invalid.html.erb +12 -0
- data/test/views/simple_projects_grid.html.erb +9 -0
- data/test/wice_grid_core_ext_test.rb +183 -0
- data/test/wice_grid_functional_test.rb +68 -0
- data/test/wice_grid_misc_test.rb +41 -0
- data/test/wice_grid_test.rb +42 -0
- data/test/wice_grid_view_helper_test.rb +12 -0
- data/wice_grid.gemspec +111 -0
- 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
|