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