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,32 @@
1
+ module WiceGrid #:nodoc:
2
+ module Generators #:nodoc:
3
+ class WiceGridAssetsJqueryGenerator < Rails::Generators::Base #:nodoc:
4
+
5
+ desc "Copy WiceGrid assets for JQuery based apps"
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+ def active_js_framework #:nodoc:
9
+ 'jquery'
10
+ end
11
+ def inactive_js_framework #:nodoc:
12
+ 'prototype'
13
+ end
14
+
15
+
16
+ def copy_stuff #:nodoc:
17
+ template 'wice_grid_config.rb', 'config/initializers/wice_grid_config.rb'
18
+
19
+ copy_file 'wice_grid.yml', 'config/locales/wice_grid.yml'
20
+
21
+ copy_file 'wice_grid_jquery.js', 'public/javascripts/wice_grid.js'
22
+ copy_file 'wice_grid.css', 'public/stylesheets/wice_grid.css'
23
+
24
+ %w(arrow_down.gif calendar_view_month.png expand.png page_white_find.png table_refresh.png
25
+ arrow_up.gif delete.png page_white_excel.png table.png tick_all.png untick_all.png ).each do |f|
26
+ copy_file "icons/#{f}", "public/images/icons/grid/#{f}"
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ module WiceGrid #:nodoc:
2
+ module Generators #:nodoc:
3
+ class WiceGridAssetsPrototypeGenerator < Rails::Generators::Base #:nodoc:
4
+
5
+ desc "Copy WiceGrid assets for Prototype based apps"
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+
9
+ def active_js_framework #:nodoc:
10
+ 'prototype'
11
+ end
12
+ def inactive_js_framework #:nodoc:
13
+ 'jquery'
14
+ end
15
+
16
+ def copy_stuff #:nodoc:
17
+ template 'wice_grid_config.rb', 'config/initializers/wice_grid_config.rb'
18
+
19
+ copy_file 'wice_grid.yml', 'config/locales/wice_grid.yml'
20
+
21
+ copy_file 'wice_grid_prototype.js', 'public/javascripts/wice_grid.js'
22
+ copy_file 'calendarview.js', 'public/javascripts/calendarview.js'
23
+ copy_file 'wice_grid.css', 'public/stylesheets/wice_grid.css'
24
+ copy_file 'calendarview.css', 'public/stylesheets/calendarview.css'
25
+
26
+ %w(arrow_down.gif calendar_view_month.png expand.png page_white_find.png table_refresh.png
27
+ arrow_up.gif delete.png page_white_excel.png table.png tick_all.png untick_all.png ).each do |f|
28
+ copy_file "icons/#{f}", "public/images/icons/grid/#{f}"
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: UTF-8
2
+ module Wice
3
+
4
+ class GridOutputBuffer < String #:nodoc:
5
+
6
+ attr_reader :stubborn_output_mode
7
+ attr_accessor :return_empty_strings_for_nonexistent_filters
8
+
9
+ def stubborn_output_mode=(m)
10
+ RAILS_DEFAULT_LOGGER.debug("=== WiceGrid: detached filters are requested, postponing output till the second call of the view helper") if m
11
+ @stubborn_output_mode = m
12
+ end
13
+
14
+ def initialize(*attrs)
15
+ super(*attrs)
16
+ @filters = HashWithIndifferentAccess.new
17
+ @first_output = false
18
+ @stubborn_output_mode = false
19
+ end
20
+
21
+ def to_s
22
+ if @first_output || ! @stubborn_output_mode
23
+ super.html_safe_if_necessary
24
+ else
25
+ @first_output = true
26
+ ''
27
+ end
28
+ end
29
+
30
+ def add_filter(detach_with_id, filter_code)
31
+ raise WiceGridException.new("Detached ID #{detach_with_id} is already used!") if @filters.has_key? detach_with_id
32
+ @filters[detach_with_id] = filter_code
33
+ end
34
+
35
+ def filter_for detach_with_id
36
+ unless @filters.has_key? detach_with_id
37
+ if @return_empty_strings_for_nonexistent_filters
38
+ return ''
39
+ else
40
+ raise WiceGridException.new("No filter with Detached ID '#{detach_with_id}'!")
41
+ end
42
+ end
43
+ raise WiceGridException.new("Filter with Detached ID '#{detach_with_id}' has already been requested once! There cannot be two instances of the same filter on one page") if @filters[detach_with_id] == false
44
+ res = @filters[detach_with_id]
45
+ @filters[detach_with_id] = false
46
+ return res
47
+ end
48
+
49
+ alias_method :[], :filter_for
50
+
51
+ end
52
+ end
@@ -0,0 +1,535 @@
1
+ # encoding: UTF-8
2
+ module Wice
3
+ class GridRenderer
4
+
5
+ include ActionView::Helpers::TagHelper
6
+ include ActionView::Helpers::CaptureHelper
7
+ include ActionView::Helpers::TextHelper
8
+ include ActionView::Helpers::AssetTagHelper
9
+ include ActionView::Helpers::JavaScriptHelper
10
+
11
+ attr_reader :page_parameter_name
12
+ attr_reader :after_row_handler
13
+ attr_reader :before_row_handler
14
+ attr_reader :blank_slate_handler
15
+ attr_reader :grid
16
+ attr_accessor :erb_mode
17
+
18
+ attr_accessor :reset_button_present, :submit_button_present, :show_hide_button_present, :csv_export_icon_present
19
+
20
+ @@order_parameter_name = "order"
21
+ @@order_direction_parameter_name = "order_direction"
22
+ @@page_parameter_name = "page"
23
+
24
+ def initialize(grid, view) #:nodoc:
25
+ @grid = grid
26
+ @grid.renderer = self
27
+ @columns = []
28
+ @columns_table = {}
29
+ @action_column_present = false
30
+ @view = view
31
+ end
32
+
33
+ def config #:nodoc:
34
+ @view.config
35
+ end
36
+
37
+ def controller #:nodoc:
38
+ @view.controller
39
+ end
40
+
41
+
42
+ def add_column(vc) #:nodoc:
43
+ @columns_table[vc.fully_qualified_attribute_name] = vc if vc.attribute_name
44
+ @columns << vc
45
+ end
46
+
47
+ def [](k) #:nodoc:
48
+ @columns_table[k]
49
+ end
50
+
51
+ def number_of_columns(filter = nil) #:nodoc:
52
+ filter_columns(filter).size
53
+ end
54
+
55
+ def each_column_label(filter = nil) #:nodoc:
56
+ filter_columns(filter).each{|col| yield col.column_name}
57
+ end
58
+
59
+ def column_labels(filter = nil) #:nodoc:
60
+ filter_columns(filter).collect(&:column_name)
61
+ end
62
+
63
+ def each_column(filter = nil) #:nodoc:
64
+ filter_columns(filter).each{|col| yield col}
65
+ end
66
+
67
+ def contains_a_text_input? #:nodoc:
68
+ filter_columns(:in_html).detect(&:contains_a_text_input)
69
+ end
70
+
71
+ def each_column_aware_of_one_last_one(filter = nil) #:nodoc:
72
+ cols = filter_columns(filter)
73
+ cols[0..-2].each{|col| yield col, false}
74
+ yield cols.last, true
75
+ end
76
+
77
+ def last_column_for_html #:nodoc:
78
+ filter_columns(:in_html).last
79
+ end
80
+
81
+ def select_for(filter) #:nodoc:
82
+ filter_columns(filter).select{|col| yield col}
83
+ end
84
+
85
+ def find_one_for(filter) #:nodoc:
86
+ filter_columns(filter).find{|col| yield col}
87
+ end
88
+
89
+
90
+ def each_column_with_attribute #:nodoc:
91
+ @columns.select(&:attribute_name).each{|col| yield col}
92
+ end
93
+
94
+ alias_method :each, :each_column
95
+ include Enumerable
96
+
97
+ def csv_export_icon #:nodoc:
98
+ tooltip = WiceGridNlMessageProvider.get_message(:CSV_EXPORT_TOOLTIP)
99
+ @csv_export_icon_present = true
100
+ image_tag(Defaults::CSV_EXPORT_ICON,
101
+ :title => tooltip,
102
+ :class => 'clickable export_to_csv_button',
103
+ :alt => tooltip
104
+ )
105
+ end
106
+
107
+ def pagination_panel(no_rightmost_column, hide_csv_button) #:nodoc:
108
+ panel = yield
109
+
110
+ render_csv_button = @grid.export_to_csv_enabled && ! hide_csv_button
111
+
112
+ number_of_columns = self.number_of_columns(:in_html)
113
+ number_of_columns -= 1 if no_rightmost_column
114
+
115
+ if panel.nil?
116
+ if render_csv_button
117
+ "<tr><td colspan=\"#{number_of_columns}\"></td><td>#{csv_export_icon}</td></tr>"
118
+ else
119
+ ''
120
+ end
121
+ else
122
+ if render_csv_button
123
+ "<tr><td colspan=\"#{number_of_columns}\">#{panel}</td><td>#{csv_export_icon}</td></tr>"
124
+ else
125
+ "<tr><td colspan=\"#{number_of_columns + 1}\">#{panel}</td></tr>"
126
+ end
127
+ end
128
+ end
129
+
130
+ def element_to_focus #:nodoc:
131
+ grid.status['foc']
132
+ end
133
+
134
+
135
+ # Adds a column with checkboxes for each record. Useful for actions with multiple records, for example, deleting
136
+ # selected records. Please note that +action_column+ only creates the checkboxes and the 'Select All' and
137
+ # 'Deselect All' buttons, and the form itelf as well as processing the parameters should be taken care of
138
+ # by the application code.
139
+ #
140
+ # * <tt>:param_name</tt> - The name of the HTTP parameter.
141
+ # The complete HTTP parameter is <tt>"#{grid_name}[#{param_name}][]"</tt>.
142
+ # The default param_name is 'selected'.
143
+ # * <tt>:td_html_attrs</tt> - a hash of HTML attributes to be included into the <tt>td</tt> tag.
144
+ # * <tt>:select_all_buttons</tt> - show/hide buttons 'Select All' and 'Deselect All' in the column header.
145
+ # The default is +true+.
146
+ # * <tt>:object_property</tt> - a method used to obtain the value for the HTTP parameter. The default is +id+.
147
+ def action_column(opts = {})
148
+
149
+ if @action_column_present
150
+ raise Wice::WiceGridException.new('There can be only one action column in a WiceGrid')
151
+ end
152
+
153
+ options = {
154
+ :param_name => :selected,
155
+ :td_html_attrs => {},
156
+ :select_all_buttons => true,
157
+ :object_property => :id
158
+ }
159
+
160
+ opts.assert_valid_keys(options.keys)
161
+ options.merge!(opts)
162
+ @action_column_present = true
163
+ @columns << ActionViewColumn.new(@grid, options[:td_html_attrs], options[:param_name],
164
+ options[:select_all_buttons], options[:object_property], @view)
165
+ end
166
+
167
+ # Defines everything related to a column in a grid - column name, filtering, rendering cells, etc.
168
+ #
169
+ # +column+ is only used inside the block of the +grid+ method. See documentation for the +grid+ method for more details.
170
+ #
171
+ # The only parameter is a hash of options. None of them is optional. If no options are supplied, the result will be a
172
+ # column with no name, no filtering and no sorting.
173
+ #
174
+ # * <tt>:column_name</tt> - Name of the column.
175
+ # * <tt>:td_html_attrs</tt> - a hash of HTML attributes to be included into the <tt>td</tt> tag.
176
+ # * <tt>:class</tt> - a shortcut for <tt>:td_html_attrs => {:class => 'css_class'}</tt>
177
+ # * <tt>:attribute_name</tt> - name of a database column (which normally correspond to a model attribute with the
178
+ # same name). By default the field is assumed to belong to the default table (see documentation for the
179
+ # +initialize_grid+ method). Parameter <tt>:model_class</tt> allows to specify another table. Presence of
180
+ # this parameter
181
+ # * adds sorting capabilities by this field
182
+ # * automatically creates a filter based on the type of the field unless parameter <tt>:no_filter</tt> is set to true.
183
+ # The following filters exist for the following types:
184
+ # * <tt>string</tt> - a text field
185
+ # * <tt>integer</tt> and <tt>float</tt> - two text fields to specify the range. Both limits or only one
186
+ # can be specified
187
+ # * <tt>boolean</tt> - a dropdown list with 'yes', 'no', or '-'. These labels can be changed in
188
+ # <tt>lib/wice_grid_config.rb</tt>.
189
+ # * <tt>date</tt> - two sets of standard date dropdown lists so specify the time range.
190
+ # * <tt>datetime</tt> - two sets of standard datetime dropdown lists so specify the time range. This filter
191
+ # is far from being user-friendly due to the number of dropdown lists.
192
+ # * <tt>:no_filter</tt> - Turns off filters even if <tt>:attribute_name</tt> is specified.
193
+ # This is needed if sorting is required while filters are not.
194
+ # * <tt>:allow_ordering</tt> - Enable/disable ordering links in the column titles. The default is +true+
195
+ # (i.e. if <tt>:attribute_name</tt> is defined, ordering is enabled)
196
+ # * <tt>:model_class</tt> - Name of the model class to which <tt>:attribute_name</tt> belongs to if this is not the main table.
197
+ # * <tt>:table_alias</tt> - In case there are two joined assocations both referring to the same table, ActiveRecord
198
+ # constructs a query where the second join provides an alias for the joined table. Setting <tt>:table_alias</tt>
199
+ # to this alias will enable WiceGrid to order and filter by columns belonging to different associatiations but
200
+ # originating from the same table without confusion. See README for an example.
201
+ # * <tt>:custom_filter</tt> - Allows to construct a custom dropdown filter. Depending on the value of
202
+ # <tt>:custom_filter</tt> different modes are available:
203
+ # * array of strings and/or numbers - this is a direct definition of possible values of the dropdown.
204
+ # Every item will be used both as the value of the select option and as its label.
205
+ # * Array of two-element arrays - Every first item of the two-element array is used for the label of the select option
206
+ # while the second element is the value of the select option
207
+ # * Hash - The keys of the hash become the labels of the generated dropdown list,
208
+ # while the values will be values of options of the dropdown list:
209
+ # * <tt>:auto</tt> - a powerful option which populates the dropdown list with all unique values of the field specified by
210
+ # <tt>:attribute_name</tt> and <tt>:model_class</tt>.
211
+ # <tt>:attribute_name</tt> throughout all pages. In other words, this runs an SQL query without +offset+ and +limit+
212
+ # clauses and with <tt>distinct(table.field)</tt> instead of <tt>distinct(*)</tt>
213
+ # * any other symbol name (method name) - The dropdown list is populated by all unique value returned by the
214
+ # method with this name sent to <em>all</em> ActiveRecord objects throughout all pages. The main difference
215
+ # from <tt>:auto</tt> is that this method does not have to be a field in the result set, it is just some
216
+ # value computed in the method after the database call and ActiveRecord instantiation.
217
+ #
218
+ # But here lies the major drawback - this mode requires additional query without +offset+ and +limit+
219
+ # clauses to instantiate _all_ ActiveRecord objects, and performance-wise it brings all the advantages
220
+ # of pagination to nothing. Thus, memory- and performance-wise this can be really bad for some queries
221
+ # and tables and should be used with care.
222
+ #
223
+ # If the method returns a atomic value like a string or an integer, it is used for both the value and the
224
+ # label of the select option element. However, if the retuned value is a two element array, the first element
225
+ # is used for the option label and the second - for the value.
226
+ # Read more in README, section 'Custom dropdown filters'
227
+ # * An array of symbols (method names) - similar to the mode with a single symbol name. The first method name
228
+ # is sent to the ActiveRecord object if it responds to this method, the second method name is sent to the
229
+ # returned value unless it is +nil+, and so on. In other words, a single symbol mode is a
230
+ # case of an array of symbols where the array contains just one element. Thus the warning about the single method name
231
+ # mode applies here as well.
232
+ #
233
+ # If the last method returns a atomic value like a string or an integer, it is used for both the value and the
234
+ # label of the select option element.
235
+ # However, if the retuned value is a two element array, the first element is used for the option label and the
236
+ # second - for the value.
237
+ # Read more in README, section 'Custom dropdown filters'
238
+ # * <tt>:boolean_filter_true_label</tt> - overrides the label for <tt>true</tt> in the boolean filter (<tt>wice_grid.boolean_filter_true_label</tt> in <tt>wice_grid.yml</tt>).
239
+ # * <tt>:boolean_filter_false_label</tt> - overrides the label for <tt>false</tt> in the boolean filter (<tt>wice_grid.boolean_filter_false_label</tt> in <tt>wice_grid.yml</tt>).
240
+ # * <tt>:allow_multiple_selection</tt> - enables or disables switching between single and multiple selection modes for
241
+ # custom dropdown boxes. +true+ by default (see +ALLOW_MULTIPLE_SELECTION+ in the configuration file).
242
+ # * <tt>:filter_all_label</tt> - overrides the default value for <tt>BOOLEAN_FILTER_FALSE_LABEL</tt> ('<tt>--</tt>')
243
+ # in the config. Has effect in a column with a boolean filter _or_ a custom filter.
244
+ # * <tt>:detach_with_id</tt> - allows to detach the filter and render it after or before the grid with the
245
+ # +grid_filter+ helper. The value is an arbitrary unique identifier
246
+ # of the filter. Read section 'Detached Filters' in README for details.
247
+ # Has effect in a column with a boolean filter _or_ a custom filter.
248
+ # * <tt>:in_csv</tt> - When CSV export is enabled, all columns are included into the export. Setting <tt>:in_csv</tt>
249
+ # to false will prohibit the column from inclusion into the export.
250
+ # * <tt>:in_html</tt> - When CSV export is enabled and it is needed to use a column for CSV export only and ignore it
251
+ # in HTML, set this parameter to false.
252
+ # * <tt>:helper_style</tt> - Changes the flavor of Date and DateTime filters. The values are:
253
+ # * <tt>:standard</tt> - the default Rails Date/DateTime helper
254
+ # * <tt>:calendar</tt> - a Javascript popup calendar control
255
+ # * <tt>:negation_in_filter</tt> - turn on/off the negation checkbox in string filters
256
+ # * <tt>:auto_reload</tt> - a boolean value specifying if a change in a filter triggers reloading of the grid. Works with all
257
+ # filter types including the JS calendar, the only exception is the standard Rails date/datetime filters.
258
+ # The default is false. (see +AUTO_RELOAD+ in the configuration file).
259
+ #
260
+ # The block parameter is an ActiveRecord instance. This block is called for every ActiveRecord shown, and the return
261
+ # value of the block is a string which becomes the contents of one table cell, or an array of two elements where
262
+ # the first element is the cell contents and the second is a hash of HTML attributes to be added for the <tt><td></tt>
263
+ # tag of the current cell.
264
+ #
265
+ # In case of an array output, please note that if you need to define HTML attributes for all <tt><td></tt>'s in a
266
+ # column, use +td_html_attrs+. Also note that if the method returns a hash with a <tt>:class</tt> or <tt>'class'</tt>
267
+ # element, it will not overwrite the class defined in +td_html_attrs+, or classes added by the grid itself
268
+ # (+active_filter+ and +sorted+), instead they will be all concatenated:
269
+ # <tt><td class="sorted user_class_for_columns user_class_for_this_specific_cell"></tt>
270
+ #
271
+ # It is up to the developer to make sure that what in rendered in column cells
272
+ # corresponds to sorting and filtering specified by parameters <tt>:attribute_name</tt> and <tt>:model_class</tt>.
273
+
274
+ def column(opts = {}, &block)
275
+ options = {
276
+ :allow_multiple_selection => Defaults::ALLOW_MULTIPLE_SELECTION,
277
+ :allow_ordering => true,
278
+ :attribute_name => nil,
279
+ :auto_reload => Defaults::AUTO_RELOAD,
280
+ :boolean_filter_false_label => WiceGridNlMessageProvider.get_message(:BOOLEAN_FILTER_FALSE_LABEL),
281
+ :boolean_filter_true_label => WiceGridNlMessageProvider.get_message(:BOOLEAN_FILTER_TRUE_LABEL),
282
+ :class => nil,
283
+ :column_name => '',
284
+ :custom_filter => nil,
285
+ :custom_order => nil,
286
+ :detach_with_id => nil,
287
+ :filter_all_label => Defaults::CUSTOM_FILTER_ALL_LABEL,
288
+ :helper_style => Defaults::HELPER_STYLE,
289
+ :in_csv => true,
290
+ :in_html => true,
291
+ :model_class => nil,
292
+ :negation_in_filter => Defaults::NEGATION_IN_STRING_FILTERS,
293
+ :no_filter => false,
294
+ :table_alias => nil,
295
+ :td_html_attrs => {}
296
+ }
297
+
298
+ opts.assert_valid_keys(options.keys)
299
+ options.merge!(opts)
300
+
301
+ unless options[:model_class].nil?
302
+ options[:model_class] = options[:model_class].constantize if options[:model_class].is_a? String
303
+ raise WiceGridArgumentError.new("Option :model_class can be either a class or a string instance") unless options[:model_class].is_a? Class
304
+ end
305
+
306
+ if options[:attribute_name].nil? && options[:model_class]
307
+ raise WiceGridArgumentError.new("Option :model_class is only used together with :attribute_name")
308
+ end
309
+
310
+ if options[:attribute_name] && options[:attribute_name].index('.')
311
+ raise WiceGridArgumentError.new("Invalid attribute name #{options[:attribute_name]}. An attribute name must not contain a table name!")
312
+ end
313
+
314
+ if options[:class]
315
+ options[:td_html_attrs].add_or_append_class_value!(options[:class])
316
+ options.delete(:class)
317
+ end
318
+
319
+ if block.nil?
320
+ if ! options[:attribute_name].blank?
321
+ block = lambda{|obj| obj.send(options[:attribute_name])}
322
+ else
323
+ raise WiceGridArgumentError.new(
324
+ "Missing column block without attribute_name defined. You can only omit the block if attribute_name is present.")
325
+ end
326
+ end
327
+
328
+ klass = ViewColumn
329
+ if options[:attribute_name] &&
330
+ col_type_and_table_name = @grid.declare_column(options[:attribute_name], options[:model_class],
331
+ options[:custom_filter], options[:table_alias])
332
+
333
+ db_column, table_name, main_table = col_type_and_table_name
334
+ col_type = db_column.type
335
+
336
+ if options[:custom_filter]
337
+
338
+ custom_filter = if options[:custom_filter] == :auto
339
+ lambda{ @grid.distinct_values_for_column(db_column) } # Thank God Ruby has higher order functions!!!
340
+
341
+ elsif options[:custom_filter].class == Symbol
342
+ lambda{ @grid.distinct_values_for_column_in_resultset([options[:custom_filter]])}
343
+
344
+ elsif options[:custom_filter].class == Hash
345
+ options[:custom_filter].keys
346
+
347
+ options[:custom_filter].to_a
348
+
349
+ elsif options[:custom_filter].class == Array
350
+ if options[:custom_filter].empty?
351
+ []
352
+ elsif options[:custom_filter].all_items_are_of_class(Symbol)
353
+ lambda{ @grid.distinct_values_for_column_in_resultset(options[:custom_filter]) }
354
+
355
+ elsif options[:custom_filter].all_items_are_of_class(String) || options[:custom_filter].all_items_are_of_class(Numeric)
356
+ options[:custom_filter].map{|i| [i,i]}
357
+
358
+ elsif options[:custom_filter].all_items_are_of_class(Array)
359
+ options[:custom_filter]
360
+ else
361
+ raise WiceGridArgumentError.new(
362
+ ':custom_filter can equal :auto, an array of string and/or numbers (direct values for the dropdown), ' +
363
+ 'a homogeneous array of symbols (a sequence of methods to send to AR objects in the result set to ' +
364
+ 'retrieve unique values for the dropdown), a Symbol (a shortcut for a one member array of symbols), ' +
365
+ 'a hash where keys are labels and values are values for the dropdown option, or an array of two-item arrays, ' +
366
+ 'each of which contains the label (first element) and the value (second element) for a dropdown option'
367
+ )
368
+ end
369
+ end
370
+
371
+ klass = ViewColumnCustomDropdown
372
+ else
373
+ klass = ViewColumn.handled_type[col_type] || ViewColumn
374
+ end # custom_filter
375
+ end # attribute_name
376
+
377
+ vc = klass.new(block, options, @grid, table_name, main_table, custom_filter, @view)
378
+
379
+ vc.negation = options[:negation_in_filter] if vc.respond_to? :negation=
380
+
381
+ vc.filter_all_label = options[:filter_all_label] if vc.kind_of?(ViewColumnCustomDropdown)
382
+ if vc.kind_of?(ViewColumnBoolean)
383
+ vc.boolean_filter_true_label = options[:boolean_filter_true_label]
384
+ vc.boolean_filter_false_label = options[:boolean_filter_false_label]
385
+ end
386
+ add_column(vc)
387
+ end
388
+
389
+ # Optional method inside the +grid+ block, to which every ActiveRecord instance is injected, just like +column+.
390
+ # Unlike +column+, it returns a hash which will be used as HTML attributes for the row with the given ActiveRecord instance.
391
+ #
392
+ # Note that if the method returns a hash with a <tt>:class</tt> or <tt>'class'</tt> element, it will not overwrite
393
+ # classes +even+ and +odd+, instead they will be concatenated: <tt><tr class="even highlighted_row_class_name"></tt>
394
+ def row_attributes(&block)
395
+ @row_attributes_handler = block
396
+ end
397
+
398
+ # Can be used to add HTML code (another row, for example) right after each grid row.
399
+ # Nothing is added if the block return +false+ or +nil+.
400
+ def after_row(&block)
401
+ @after_row_handler = block
402
+ end
403
+
404
+ # Can be used to add HTML code (another row, for example) right before each grid row.
405
+ # Nothing is added if the block return +false+ or +nil+.
406
+ def before_row(&block)
407
+ @before_row_handler = block
408
+ end
409
+
410
+ # The output of the block submitted to +blank_slate+ is rendered instead of the whole grid if no filters are active
411
+ # and there are no records to render.
412
+ # In addition to the block style two other variants are accepted:
413
+ # * <tt>g.blank_slate "some text to be rendered"</tt>
414
+ # * <tt>g.blank_slate :partial => "partial_name"</tt>
415
+ def blank_slate(opts = nil, &block)
416
+ if (opts.is_a?(Hash) && opts.has_key?(:partial) && block.nil?) || (opts.is_a?(String) && block.nil?)
417
+ @blank_slate_handler = opts
418
+ elsif opts.nil? && block
419
+ @blank_slate_handler = block
420
+ else
421
+ raise WiceGridArgumentError.new("blank_slate accepts only a string, a block, or :template => 'path_to_template' ")
422
+ end
423
+ end
424
+
425
+
426
+ def get_row_attributes(ar_object) #:nodoc:
427
+ if @row_attributes_handler
428
+ row_attributes = @row_attributes_handler.call(ar_object)
429
+ row_attributes = {} if row_attributes.blank?
430
+ unless row_attributes.is_a?(Hash)
431
+ raise WiceGridArgumentError.new("row_attributes block must return a hash containing HTML attributes. The returned value is #{row_attributes.inspect}")
432
+ end
433
+ row_attributes
434
+ else
435
+ {}
436
+ end
437
+ end
438
+
439
+
440
+ def no_filter_needed? #:nodoc:
441
+ not @columns.inject(false){|a,b| a || b.filter_shown? }
442
+ end
443
+
444
+ def no_filter_needed_in_main_table? #:nodoc:
445
+ not @columns.inject(false){|a,b| a || b.filter_shown_in_main_table? }
446
+ end
447
+
448
+ def base_link_for_filter(controller, extra_parameters = {}) #:nodoc:
449
+ new_params = controller.params.deep_clone_yl
450
+ new_params.merge!(extra_parameters)
451
+
452
+ if new_params[@grid.name]
453
+ new_params[@grid.name].delete(:page) # we reset paging here
454
+ new_params[@grid.name].delete(:f) # no filter for the base url
455
+ new_params[@grid.name].delete(:foc) # nullify the focus
456
+ new_params[@grid.name].delete(:q) # and no request for the saved query
457
+ end
458
+
459
+ new_params[:only_path] = false
460
+ base_link_with_pp_info = controller.url_for(new_params).gsub(/\?+$/,'')
461
+
462
+ if new_params[@grid.name]
463
+ new_params[@grid.name].delete(:pp) # and reset back to pagination if show all mode is on
464
+ end
465
+ [base_link_with_pp_info, controller.url_for(new_params).gsub(/\?+$/,'')]
466
+ end
467
+
468
+
469
+
470
+ def link_for_export(controller, format, extra_parameters = {}) #:nodoc:
471
+ new_params = controller.params.deep_clone_yl
472
+ new_params.merge!(extra_parameters)
473
+
474
+ new_params[@grid.name] = {} unless new_params[@grid.name]
475
+ new_params[@grid.name][:export] = format
476
+
477
+ new_params[:only_path] = false
478
+ controller.url_for(new_params)
479
+ end
480
+
481
+
482
+ def column_link(column, direction, params, extra_parameters = {}) #:nodoc:
483
+
484
+ column_attribute_name = if column.attribute_name.index('.') or column.main_table
485
+ column.attribute_name
486
+ else
487
+ column.table_alias_or_table_name + '.' + column.attribute_name
488
+ end
489
+
490
+ query_params = {@grid.name => {
491
+ @@order_parameter_name => column_attribute_name,
492
+ @@order_direction_parameter_name => direction
493
+ }}
494
+
495
+ cleaned_params = params.deep_clone_yl
496
+ cleaned_params.merge!(extra_parameters)
497
+
498
+ cleaned_params.delete(:controller)
499
+ cleaned_params.delete(:action)
500
+
501
+
502
+ query_params = cleaned_params.rec_merge(query_params)
503
+
504
+ '?' + query_params.to_query
505
+ end
506
+
507
+ def contains_auto_reloading_inputs #:nodoc:
508
+ contains_auto_reloading_elements(:has_auto_reloading_input?)
509
+ end
510
+
511
+ def contains_auto_reloading_inputs_with_negation_checkboxes #:nodoc:
512
+ contains_auto_reloading_elements(:auto_reloading_input_with_negation_checkbox?)
513
+ end
514
+
515
+ def contains_auto_reloading_selects #:nodoc:
516
+ contains_auto_reloading_elements(:has_auto_reloading_select?)
517
+ end
518
+
519
+ def contains_auto_reloading_calendars #:nodoc:
520
+ contains_auto_reloading_elements(:has_auto_reloading_calendar?)
521
+ end
522
+
523
+ protected
524
+
525
+ def contains_auto_reloading_elements(what) #:nodoc:
526
+ filter_columns(:in_html).detect{|column| column.filter_shown? && column.send(what)}
527
+ end
528
+
529
+
530
+ def filter_columns(method_name = nil) #:nodoc:
531
+ method_name ? @columns.select(&method_name) : @columns
532
+ end
533
+
534
+ end
535
+ end