will_filter 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.loadpath +12 -0
- data/.project +18 -0
- data/CHANGELOG.rdoc +1 -0
- data/Gemfile +32 -0
- data/Gemfile.lock +73 -0
- data/LICENSE +18 -0
- data/README.rdoc +74 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/app/controllers/application_controller.rb +27 -0
- data/app/controllers/will_filter/calendar_controller.rb +31 -0
- data/app/controllers/will_filter/exporter_controller.rb +122 -0
- data/app/controllers/will_filter/filter_controller.rb +107 -0
- data/app/helpers/application_helper.rb +26 -0
- data/app/models/will_filter/filter.rb +694 -0
- data/app/views/layouts/application.html.erb +11 -0
- data/app/views/will_filter/calendar/_annual.html.erb +14 -0
- data/app/views/will_filter/calendar/_month.html.erb +39 -0
- data/app/views/will_filter/calendar/_quarter.html.erb +15 -0
- data/app/views/will_filter/calendar/index.html.erb +52 -0
- data/app/views/will_filter/common/_actions_bar.html.erb +5 -0
- data/app/views/will_filter/common/_results_table.html.erb +129 -0
- data/app/views/will_filter/common/_scripts.html.erb +10 -0
- data/app/views/will_filter/exporter/export.html.erb +11 -0
- data/app/views/will_filter/exporter/index.html.erb +47 -0
- data/app/views/will_filter/filter/_condition.html.erb +30 -0
- data/app/views/will_filter/filter/_conditions.html.erb +56 -0
- data/app/views/will_filter/filter/_container.html.erb +31 -0
- data/app/views/will_filter/filter/containers/_blank.html.erb +1 -0
- data/app/views/will_filter/filter/containers/_boolean.html.erb +5 -0
- data/app/views/will_filter/filter/containers/_date.html.erb +12 -0
- data/app/views/will_filter/filter/containers/_date_range.html.erb +20 -0
- data/app/views/will_filter/filter/containers/_date_time.html.erb +12 -0
- data/app/views/will_filter/filter/containers/_date_time_range.html.erb +20 -0
- data/app/views/will_filter/filter/containers/_list.html.erb +7 -0
- data/app/views/will_filter/filter/containers/_numeric_range.html.erb +13 -0
- data/app/views/will_filter/filter/containers/_text.html.erb +7 -0
- data/app/views/will_filter/filter/index.html.erb +4 -0
- data/config/application.rb +45 -0
- data/config/boot.rb +6 -0
- data/config/database.yml +22 -0
- data/config/environment.rb +13 -0
- data/config/environments/development.rb +26 -0
- data/config/environments/production.rb +49 -0
- data/config/environments/test.rb +38 -0
- data/config/routes.rb +64 -0
- data/config/will_filter/config.yml +97 -0
- data/config.ru +4 -0
- data/db/development.sqlite3 +0 -0
- data/db/migrate/20090730070119_create_will_filter_tables.rb +19 -0
- data/db/seeds.rb +7 -0
- data/db/test.sqlite3 +0 -0
- data/doc/README_FOR_APP +2 -0
- data/examples/README +1 -0
- data/lib/application_helper.rb +45 -0
- data/lib/core_ext/active_record/base.rb +44 -0
- data/lib/core_ext/array.rb +34 -0
- data/lib/core_ext/object.rb +34 -0
- data/lib/generators/will_filter/templates/config.yml +97 -0
- data/lib/generators/will_filter/templates/create_will_filter_tables.rb +19 -0
- data/lib/generators/will_filter/will_filter_generator.rb +24 -0
- data/lib/tasks/.gitkeep +0 -0
- data/lib/tasks/will_filter_tasks.rake +32 -0
- data/lib/will_filter/calendar.rb +168 -0
- data/lib/will_filter/common_methods.rb +49 -0
- data/lib/will_filter/config.rb +104 -0
- data/lib/will_filter/containers/boolean.rb +43 -0
- data/lib/will_filter/containers/date.rb +51 -0
- data/lib/will_filter/containers/date_range.rb +56 -0
- data/lib/will_filter/containers/date_time.rb +50 -0
- data/lib/will_filter/containers/date_time_range.rb +64 -0
- data/lib/will_filter/containers/filter_list.rb +59 -0
- data/lib/will_filter/containers/list.rb +56 -0
- data/lib/will_filter/containers/nil.rb +45 -0
- data/lib/will_filter/containers/numeric.rb +52 -0
- data/lib/will_filter/containers/numeric_delimited.rb +50 -0
- data/lib/will_filter/containers/numeric_range.rb +60 -0
- data/lib/will_filter/containers/single_date.rb +57 -0
- data/lib/will_filter/containers/text.rb +45 -0
- data/lib/will_filter/containers/text_delimited.rb +51 -0
- data/lib/will_filter/engine.rb +11 -0
- data/lib/will_filter/filter_condition.rb +59 -0
- data/lib/will_filter/filter_container.rb +73 -0
- data/lib/will_filter/filter_exception.rb +27 -0
- data/lib/will_filter.rb +15 -0
- data/pkg/will_filter-0.1.0.gem +0 -0
- data/pkg/will_filter-0.1.1.gem +0 -0
- data/public/404.html +26 -0
- data/public/422.html +26 -0
- data/public/500.html +26 -0
- data/public/favicon.ico +0 -0
- data/public/robots.txt +5 -0
- data/public/will_filter/images/buttons.png +0 -0
- data/public/will_filter/images/calendar.png +0 -0
- data/public/will_filter/images/clock.png +0 -0
- data/public/will_filter/images/close.gif +0 -0
- data/public/will_filter/images/results_table_th_active.gif +0 -0
- data/public/will_filter/images/sort_arrow_all.gif +0 -0
- data/public/will_filter/images/sort_bg.gif +0 -0
- data/public/will_filter/images/spinner.gif +0 -0
- data/public/will_filter/javascripts/will_filter.js +568 -0
- data/public/will_filter/javascripts/will_filter_prototype_effects.js +15 -0
- data/public/will_filter/stylesheets/will_filter.css +168 -0
- data/script/rails +6 -0
- data/test/functional/models/will_filter/filter_test.rb +297 -0
- data/test/performance/browsing_test.rb +9 -0
- data/test/test_helper.rb +71 -0
- data/uninstall.rb +24 -0
- data/will_filter.gemspec +7 -0
- metadata +208 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2011 Michael Berkovich
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
# a copy of this software and associated documentation files (the
|
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
+
# the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be
|
|
13
|
+
# included in all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
#++
|
|
23
|
+
|
|
24
|
+
module WillFilter
|
|
25
|
+
class Filter < ActiveRecord::Base
|
|
26
|
+
set_table_name :wf_filters
|
|
27
|
+
serialize :data
|
|
28
|
+
before_save :prepare_save
|
|
29
|
+
after_find :process_find
|
|
30
|
+
|
|
31
|
+
#############################################################################
|
|
32
|
+
# Basics
|
|
33
|
+
#############################################################################
|
|
34
|
+
def initialize(model_class)
|
|
35
|
+
super()
|
|
36
|
+
self.model_class_name = model_class.to_s
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def dup
|
|
40
|
+
super.tap {|ii| ii.conditions = self.conditions.dup}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def prepare_save
|
|
44
|
+
self.data = serialize_to_params
|
|
45
|
+
self.type = self.class.name
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def process_find
|
|
49
|
+
@errors = {}
|
|
50
|
+
deserialize_from_params(self.data)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
#############################################################################
|
|
54
|
+
# Defaults
|
|
55
|
+
#############################################################################
|
|
56
|
+
def show_export_options?
|
|
57
|
+
WillFilter::Config.exporting_enabled?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def show_save_options?
|
|
61
|
+
WillFilter::Config.saving_enabled?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def match
|
|
65
|
+
@match ||= :all
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def key
|
|
69
|
+
@key ||= ''
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def errors
|
|
73
|
+
@errors ||= {}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def format
|
|
77
|
+
@format ||= :html
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def fields
|
|
81
|
+
@fields ||= []
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#############################################################################
|
|
85
|
+
# a list of indexed fields where at least one of them has to be in a query
|
|
86
|
+
# otherwise the filter may hang the database
|
|
87
|
+
#############################################################################
|
|
88
|
+
def required_condition_keys
|
|
89
|
+
[]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def model_class
|
|
93
|
+
return nil unless model_class_name
|
|
94
|
+
@model_class ||= model_class_name.constantize
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def table_name
|
|
98
|
+
model_class.table_name
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def key=(new_key)
|
|
102
|
+
@key = new_key
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def match=(new_match)
|
|
106
|
+
@match = new_match
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
#############################################################################
|
|
110
|
+
# Inner Joins come in a form of
|
|
111
|
+
# [[joining_model_name, column_name], [joining_model_name, column_name]]
|
|
112
|
+
#############################################################################
|
|
113
|
+
def inner_joins
|
|
114
|
+
[]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def model_columns
|
|
118
|
+
model_class.columns
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def model_column_keys
|
|
122
|
+
model_columns.collect{|col| col.name.to_sym}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def contains_column?(key)
|
|
126
|
+
model_column_keys.index(key) != nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def definition
|
|
130
|
+
@definition ||= begin
|
|
131
|
+
defs = {}
|
|
132
|
+
model_columns.each do |col|
|
|
133
|
+
defs[col.name.to_sym] = default_condition_definition_for(col.name, col.sql_type)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
inner_joins.each do |inner_join|
|
|
137
|
+
join_class = inner_join.first.to_s.camelcase.constantize
|
|
138
|
+
join_class.columns.each do |col|
|
|
139
|
+
defs[:"#{join_class.to_s.underscore}.#{col.name.to_sym}"] = default_condition_definition_for(col.name, col.sql_type)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
defs
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def container_by_sql_type(type)
|
|
148
|
+
raise WillFilter::FilterException.new("Unsupported data type #{type}") unless WillFilter::Config.data_types[type]
|
|
149
|
+
WillFilter::Config.data_types[type]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def default_condition_definition_for(name, sql_data_type)
|
|
153
|
+
type = sql_data_type.split(" ").first.split("(").first.downcase
|
|
154
|
+
containers = container_by_sql_type(type)
|
|
155
|
+
operators = {}
|
|
156
|
+
containers.each do |c|
|
|
157
|
+
raise WillFilter::FilterException.new("Unsupported container implementation for #{c}") unless WillFilter::Config.containers[c]
|
|
158
|
+
container_klass = WillFilter::Config.containers[c].constantize
|
|
159
|
+
container_klass.operators.each do |o|
|
|
160
|
+
operators[o] = c
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
if name == "id"
|
|
165
|
+
operators[:is_filtered_by] = :filter_list
|
|
166
|
+
elsif "_id" == name[-3..-1]
|
|
167
|
+
begin
|
|
168
|
+
name[0..-4].camelcase.constantize
|
|
169
|
+
operators[:is_filtered_by] = :filter_list
|
|
170
|
+
rescue
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
operators
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def sorted_operators(opers)
|
|
178
|
+
(WillFilter::Config.operator_order & opers.keys.collect{|o| o.to_s})
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def first_sorted_operator(opers)
|
|
182
|
+
sorted_operators(opers).first.to_sym
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def default_order
|
|
186
|
+
'id'
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def order
|
|
190
|
+
@order ||= default_order
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def default_order_type
|
|
194
|
+
'desc'
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def order_type
|
|
198
|
+
@order_type ||= default_order_type
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def order_clause
|
|
202
|
+
"#{order} #{order_type}"
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def column_sorted?(key)
|
|
206
|
+
key.to_s == order
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def default_per_page
|
|
210
|
+
30
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def per_page
|
|
214
|
+
@per_page ||= default_per_page
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def page
|
|
218
|
+
@page ||= 1
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def default_per_page_options
|
|
222
|
+
[10, 20, 30, 40, 50, 100]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def per_page_options
|
|
226
|
+
@per_page_options ||= default_per_page_options.collect{ |n| [n.to_s, n.to_s] }
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def match_options
|
|
230
|
+
[["all", "all"], ["any", "any"]]
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def order_type_options
|
|
234
|
+
[["desc", "desc"], ["asc", "asc"]]
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
#############################################################################
|
|
238
|
+
# Can be overloaded for custom titles
|
|
239
|
+
#############################################################################
|
|
240
|
+
def condition_title_for(key)
|
|
241
|
+
title = key.to_s.gsub(".", ": ").gsub("_", " ")
|
|
242
|
+
title.split(" ").collect{|part| part.capitalize}.join(" ")
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def condition_options
|
|
246
|
+
@condition_options ||= begin
|
|
247
|
+
opts = []
|
|
248
|
+
definition.keys.each do |cond|
|
|
249
|
+
opts << [condition_title_for(cond), cond.to_s]
|
|
250
|
+
end
|
|
251
|
+
opts.sort_by{ |i| i[0] }
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def operator_options_for(condition_key)
|
|
256
|
+
condition_key = condition_key.to_sym if condition_key.is_a?(String)
|
|
257
|
+
|
|
258
|
+
opers = definition[condition_key]
|
|
259
|
+
raise WillFilter::FilterException.new("Invalid condition #{condition_key} for filter #{self.class.name}") unless opers
|
|
260
|
+
sorted_operators(opers).collect{|o| [o.to_s.gsub('_', ' '), o]}
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# called by the list container, should be overloaded in a subclass
|
|
264
|
+
def value_options_for(condition_key)
|
|
265
|
+
[]
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def container_for(condition_key, operator_key)
|
|
269
|
+
condition_key = condition_key.to_sym if condition_key.is_a?(String)
|
|
270
|
+
|
|
271
|
+
opers = definition[condition_key]
|
|
272
|
+
raise WillFilter::FilterException.new("Invalid condition #{condition_key} for filter #{self.class.name}") unless opers
|
|
273
|
+
oper = opers[operator_key]
|
|
274
|
+
|
|
275
|
+
# if invalid operator_key was passed, use first operator
|
|
276
|
+
oper = opers[first_sorted_operator(opers)] unless oper
|
|
277
|
+
oper
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def add_condition(condition_key, operator_key, values = [])
|
|
281
|
+
add_condition_at(size, condition_key, operator_key, values)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def valid_operator?(condition_key, operator_key)
|
|
285
|
+
condition_key = condition_key.to_sym if condition_key.is_a?(String)
|
|
286
|
+
opers = definition[condition_key]
|
|
287
|
+
return false unless opers
|
|
288
|
+
opers[operator_key]!=nil
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def add_condition_at(index, condition_key, operator_key, values = [])
|
|
292
|
+
values = [values] unless values.instance_of?(Array)
|
|
293
|
+
values = values.collect{|v| v.to_s}
|
|
294
|
+
|
|
295
|
+
condition_key = condition_key.to_sym if condition_key.is_a?(String)
|
|
296
|
+
|
|
297
|
+
unless valid_operator?(condition_key, operator_key)
|
|
298
|
+
opers = definition[condition_key]
|
|
299
|
+
operator_key = first_sorted_operator(opers)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
condition = WillFilter::FilterCondition.new(self, condition_key, operator_key, container_for(condition_key, operator_key), values)
|
|
303
|
+
@conditions.insert(index, condition)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
#############################################################################
|
|
307
|
+
# options always go in [NAME, KEY] format
|
|
308
|
+
#############################################################################
|
|
309
|
+
def default_condition_key
|
|
310
|
+
condition_options.first.last
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
#############################################################################
|
|
314
|
+
# options always go in [NAME, KEY] format
|
|
315
|
+
#############################################################################
|
|
316
|
+
def default_operator_key(condition_key)
|
|
317
|
+
operator_options_for(condition_key).first.last
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def conditions=(new_conditions)
|
|
321
|
+
@conditions = new_conditions
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def conditions
|
|
325
|
+
@conditions ||= []
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def condition_at(index)
|
|
329
|
+
conditions[index]
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def condition_by_key(key)
|
|
333
|
+
conditions.each do |c|
|
|
334
|
+
return c if c.key==key
|
|
335
|
+
end
|
|
336
|
+
nil
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def size
|
|
340
|
+
conditions.size
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def add_default_condition_at(index)
|
|
344
|
+
add_condition_at(index, default_condition_key, default_operator_key(default_condition_key))
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def remove_condition_at(index)
|
|
348
|
+
conditions.delete_at(index)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def remove_all
|
|
352
|
+
@conditions = []
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
#############################################################################
|
|
356
|
+
# Serialization
|
|
357
|
+
#############################################################################
|
|
358
|
+
def serialize_to_params(merge_params = {})
|
|
359
|
+
params = {}
|
|
360
|
+
params[:wf_type] = self.class.name
|
|
361
|
+
params[:wf_match] = match
|
|
362
|
+
params[:wf_model] = model_class_name
|
|
363
|
+
params[:wf_order] = order
|
|
364
|
+
params[:wf_order_type] = order_type
|
|
365
|
+
params[:wf_per_page] = per_page
|
|
366
|
+
params[:wf_export_fields] = fields.join(',')
|
|
367
|
+
params[:wf_export_format] = format
|
|
368
|
+
|
|
369
|
+
0.upto(size - 1) do |index|
|
|
370
|
+
condition = condition_at(index)
|
|
371
|
+
condition.serialize_to_params(params, index)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
params.merge(merge_params)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
#############################################################################
|
|
378
|
+
# allows to create a filter from params only
|
|
379
|
+
#############################################################################
|
|
380
|
+
def self.deserialize_from_params(params)
|
|
381
|
+
params = HashWithIndifferentAccess.new(params) unless params.is_a?(HashWithIndifferentAccess)
|
|
382
|
+
params[:wf_type] = self.name unless params[:wf_type]
|
|
383
|
+
params[:wf_type].constantize.new(params[:wf_model]).deserialize_from_params(params)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def deserialize_from_params(params)
|
|
387
|
+
params = HashWithIndifferentAccess.new(params) unless params.is_a?(HashWithIndifferentAccess)
|
|
388
|
+
@conditions = []
|
|
389
|
+
@match = params[:wf_match] || :all
|
|
390
|
+
@key = params[:wf_key] || self.id.to_s
|
|
391
|
+
self.model_class_name = params[:wf_model] if params[:wf_model]
|
|
392
|
+
|
|
393
|
+
@per_page = params[:wf_per_page] || default_per_page
|
|
394
|
+
@page = params[:page] || 1
|
|
395
|
+
@order_type = params[:wf_order_type] || default_order_type
|
|
396
|
+
@order = params[:wf_order] || default_order
|
|
397
|
+
|
|
398
|
+
self.id = params[:wf_id].to_i unless params[:wf_id].blank?
|
|
399
|
+
self.name = params[:wf_name] unless params[:wf_name].blank?
|
|
400
|
+
|
|
401
|
+
@fields = []
|
|
402
|
+
unless params[:wf_export_fields].blank?
|
|
403
|
+
params[:wf_export_fields].split(",").each do |fld|
|
|
404
|
+
@fields << fld.to_sym
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
if params[:wf_export_format].blank?
|
|
409
|
+
@format = :html
|
|
410
|
+
else
|
|
411
|
+
@format = params[:wf_export_format].to_sym
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
i = 0
|
|
415
|
+
while params["wf_c#{i}"] do
|
|
416
|
+
conditon_key = params["wf_c#{i}"]
|
|
417
|
+
operator_key = params["wf_o#{i}"]
|
|
418
|
+
values = []
|
|
419
|
+
j = 0
|
|
420
|
+
while params["wf_v#{i}_#{j}"] do
|
|
421
|
+
values << params["wf_v#{i}_#{j}"]
|
|
422
|
+
j += 1
|
|
423
|
+
end
|
|
424
|
+
i += 1
|
|
425
|
+
add_condition(conditon_key, operator_key.to_sym, values)
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
if params[:wf_submitted] == 'true'
|
|
429
|
+
validate!
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
return self
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
#############################################################################
|
|
436
|
+
# Validations
|
|
437
|
+
#############################################################################
|
|
438
|
+
def errors?
|
|
439
|
+
(@errors and @errors.size > 0)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def empty?
|
|
443
|
+
size == 0
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def has_condition?(key)
|
|
447
|
+
condition_by_key(key) != nil
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def valid_format?
|
|
451
|
+
WillFilter::Config.default_export_formats.include?(format.to_s)
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
def required_conditions_met?
|
|
455
|
+
return true if required_condition_keys.blank?
|
|
456
|
+
sconditions = conditions.collect{|c| c.key.to_s}
|
|
457
|
+
rconditions = required_condition_keys.collect{|c| c.to_s}
|
|
458
|
+
not (sconditions & rconditions).empty?
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def validate!
|
|
462
|
+
@errors = {}
|
|
463
|
+
0.upto(size - 1) do |index|
|
|
464
|
+
condition = condition_at(index)
|
|
465
|
+
err = condition.validate
|
|
466
|
+
@errors[index] = err if err
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
unless required_conditions_met?
|
|
470
|
+
@errors[:filter] = "Filter must contain at least one of the following conditions: #{required_condition_keys.join(", ")}"
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
errors?
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
#############################################################################
|
|
477
|
+
# SQL Conditions
|
|
478
|
+
#############################################################################
|
|
479
|
+
def sql_conditions
|
|
480
|
+
@sql_conditions ||= begin
|
|
481
|
+
|
|
482
|
+
if errors?
|
|
483
|
+
all_sql_conditions = [" 1 = 2 "]
|
|
484
|
+
else
|
|
485
|
+
all_sql_conditions = [""]
|
|
486
|
+
0.upto(size - 1) do |index|
|
|
487
|
+
condition = condition_at(index)
|
|
488
|
+
sql_condition = condition.container.sql_condition
|
|
489
|
+
|
|
490
|
+
unless sql_condition
|
|
491
|
+
raise WillFilter::FilterException.new("Unsupported operator #{condition.operator_key} for container #{condition.container.class.name}")
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
if all_sql_conditions[0].size > 0
|
|
495
|
+
all_sql_conditions[0] << ( match.to_sym == :all ? " AND " : " OR ")
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
all_sql_conditions[0] << sql_condition[0]
|
|
499
|
+
sql_condition[1..-1].each do |c|
|
|
500
|
+
all_sql_conditions << c
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
all_sql_conditions
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def debug_conditions(conds)
|
|
510
|
+
all_conditions = []
|
|
511
|
+
conds.each_with_index do |c, i|
|
|
512
|
+
cond = ""
|
|
513
|
+
if i == 0
|
|
514
|
+
cond << "\"<b>#{c}</b>\""
|
|
515
|
+
else
|
|
516
|
+
cond << "<br> <b>#{i})</b> "
|
|
517
|
+
if c.is_a?(Array)
|
|
518
|
+
cond << "["
|
|
519
|
+
cond << (c.collect{|v| "\"#{v.strip}\""}.join(", "))
|
|
520
|
+
cond << "]"
|
|
521
|
+
elsif c.is_a?(Date)
|
|
522
|
+
cond << "\"#{c.strftime("%Y-%m-%d")}\""
|
|
523
|
+
elsif c.is_a?(Time)
|
|
524
|
+
cond << "\"#{c.strftime("%Y-%m-%d %H:%M:%S")}\""
|
|
525
|
+
elsif c.is_a?(Integer)
|
|
526
|
+
cond << c.to_s
|
|
527
|
+
else
|
|
528
|
+
cond << "\"#{c}\""
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
all_conditions << cond
|
|
533
|
+
end
|
|
534
|
+
all_conditions
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
def debug_sql_conditions
|
|
538
|
+
debug_conditions(sql_conditions)
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
#############################################################################
|
|
542
|
+
# Saved Filters
|
|
543
|
+
#############################################################################
|
|
544
|
+
def saved_filters(include_default = true)
|
|
545
|
+
@saved_filters ||= begin
|
|
546
|
+
filters = []
|
|
547
|
+
|
|
548
|
+
if include_default
|
|
549
|
+
filters = default_filters
|
|
550
|
+
if (filters.size > 0)
|
|
551
|
+
filters.insert(0, ["-- Select Default Filter --", "-1"])
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
if include_default
|
|
556
|
+
conditions = ["type = ? and model_class_name = ?", self.class.name, self.model_class_name]
|
|
557
|
+
else
|
|
558
|
+
conditions = ["model_class_name = ?", self.model_class_name]
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
if WillFilter::Config.user_filters_enabled?
|
|
562
|
+
conditions[0] << " and user_id = ? "
|
|
563
|
+
if WillFilter::Config.current_user and WillFilter::Config.current_user.id
|
|
564
|
+
conditions << WillFilter::Config.current_user.id
|
|
565
|
+
else
|
|
566
|
+
conditions << "0"
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
user_filters = WillFilter::Filter.find(:all, :conditions => conditions)
|
|
571
|
+
|
|
572
|
+
if user_filters.size > 0
|
|
573
|
+
filters << ["-- Select Saved Filter --", "-2"] if include_default
|
|
574
|
+
|
|
575
|
+
user_filters.each do |filter|
|
|
576
|
+
filters << [filter.name, filter.id.to_s]
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
filters
|
|
581
|
+
end
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
#############################################################################
|
|
585
|
+
# overload this method if you don't want to allow empty filters
|
|
586
|
+
#############################################################################
|
|
587
|
+
def default_filter_if_empty
|
|
588
|
+
nil
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def handle_empty_filter!
|
|
592
|
+
return unless empty?
|
|
593
|
+
return if default_filter_if_empty.nil?
|
|
594
|
+
load_filter!(default_filter_if_empty)
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def default_filters
|
|
598
|
+
[]
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def default_filter_conditions(key)
|
|
602
|
+
[]
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def load_default_filter(key)
|
|
606
|
+
default_conditions = default_filter_conditions(key)
|
|
607
|
+
return if default_conditions.nil? or default_conditions.empty?
|
|
608
|
+
|
|
609
|
+
unless default_conditions.first.is_a?(Array)
|
|
610
|
+
add_condition(*default_conditions)
|
|
611
|
+
return
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
default_conditions.each do |default_condition|
|
|
615
|
+
add_condition(*default_condition)
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def reset!
|
|
620
|
+
remove_all
|
|
621
|
+
@sql_conditions = nil
|
|
622
|
+
@results = nil
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
def load_filter!(key_or_id)
|
|
626
|
+
reset!
|
|
627
|
+
@key = key_or_id.to_s
|
|
628
|
+
|
|
629
|
+
load_default_filter(key)
|
|
630
|
+
return self unless empty?
|
|
631
|
+
|
|
632
|
+
filter = WillFilter::Filter.find_by_id(key_or_id.to_i)
|
|
633
|
+
raise WillFilter::FilterException.new("Invalid filter key #{key_or_id.to_s}") if filter.nil?
|
|
634
|
+
filter
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
#############################################################################
|
|
638
|
+
# Export Filter Data
|
|
639
|
+
#############################################################################
|
|
640
|
+
def export_formats
|
|
641
|
+
formats = []
|
|
642
|
+
formats << ["-- Generic Formats --", -1]
|
|
643
|
+
WillFilter::Config.default_export_formats.each do |frmt|
|
|
644
|
+
formats << [frmt, frmt]
|
|
645
|
+
end
|
|
646
|
+
if custom_formats.size > 0
|
|
647
|
+
formats << ["-- Custom Formats --", -2]
|
|
648
|
+
custom_formats.each do |frmt|
|
|
649
|
+
formats << frmt
|
|
650
|
+
end
|
|
651
|
+
end
|
|
652
|
+
formats
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
def custom_format?
|
|
656
|
+
custom_formats.each do |frmt|
|
|
657
|
+
return true if frmt[1].to_sym == format
|
|
658
|
+
end
|
|
659
|
+
false
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def custom_formats
|
|
663
|
+
[]
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
def process_custom_format
|
|
667
|
+
""
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
def joins
|
|
671
|
+
return nil if inner_joins.empty?
|
|
672
|
+
inner_joins.collect do |inner_join|
|
|
673
|
+
join_table_name = inner_join.first.to_s.camelcase.constantize.table_name
|
|
674
|
+
join_on_field = inner_join.last.to_s
|
|
675
|
+
"INNER JOIN #{join_table_name} ON #{join_table_name}.id = #{table_name}.#{join_on_field}"
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
def results
|
|
680
|
+
@results ||= begin
|
|
681
|
+
handle_empty_filter!
|
|
682
|
+
|
|
683
|
+
recs = model_class.paginate(:order => order_clause, :page => page, :per_page => per_page, :conditions => sql_conditions, :joins => joins)
|
|
684
|
+
recs.wf_filter = self
|
|
685
|
+
recs
|
|
686
|
+
end
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
# sums up the column for the given conditions
|
|
690
|
+
def sum(column_name)
|
|
691
|
+
model_class.sum(column_name, :conditions => sql_conditions)
|
|
692
|
+
end
|
|
693
|
+
end
|
|
694
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<table class="wf_calendar_table" cellpadding="0" cellspacing="0">
|
|
2
|
+
<tr>
|
|
3
|
+
<% 0.upto(11) do |m| %>
|
|
4
|
+
<% if m % 4 == 0 %>
|
|
5
|
+
</tr><tr>
|
|
6
|
+
<% end %>
|
|
7
|
+
<td valign="top">
|
|
8
|
+
<% next_cal = calendar.move(m.months) %>
|
|
9
|
+
<div class="wf_calendar_title"><%=next_cal.title %></div>
|
|
10
|
+
<%= render :partial => "month", :locals => {:calendar => next_cal} %>
|
|
11
|
+
</td>
|
|
12
|
+
<% end %>
|
|
13
|
+
</tr>
|
|
14
|
+
</table>
|