toolbox 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/Rakefile +57 -0
  2. data/lib/dirs.rb +9 -0
  3. data/lib/toolbox/config.rb +211 -0
  4. data/lib/toolbox/default_controller.rb +393 -0
  5. data/lib/toolbox/helpers.rb +11 -0
  6. data/lib/toolbox/rendering.rb +413 -0
  7. data/lib/toolbox/searching.rb +85 -0
  8. data/lib/toolbox/session_params.rb +63 -0
  9. data/lib/toolbox/sorting.rb +74 -0
  10. data/lib/toolbox/version.rb +4 -0
  11. data/lib/toolbox.rb +29 -0
  12. data/locale/de/LC_MESSAGES/toolbox.mo +0 -0
  13. data/public/images/add.png +0 -0
  14. data/public/images/arrow_down.gif +0 -0
  15. data/public/images/arrow_up.gif +0 -0
  16. data/public/images/close.png +0 -0
  17. data/public/images/edit.gif +0 -0
  18. data/public/images/email.png +0 -0
  19. data/public/images/page.png +0 -0
  20. data/public/images/page_acrobat.png +0 -0
  21. data/public/images/page_add.png +0 -0
  22. data/public/images/page_copy.png +0 -0
  23. data/public/images/page_delete.png +0 -0
  24. data/public/images/page_edit.png +0 -0
  25. data/public/images/page_excel.png +0 -0
  26. data/public/images/page_list.png +0 -0
  27. data/public/images/page_save.png +0 -0
  28. data/public/images/page_word.png +0 -0
  29. data/public/images/remove.png +0 -0
  30. data/public/images/show.gif +0 -0
  31. data/public/images/spinner.gif +0 -0
  32. data/public/javascripts/popup.js +498 -0
  33. data/public/javascripts/toolbox.js +18 -0
  34. data/public/stylesheets/context_menu.css +168 -0
  35. data/public/stylesheets/popup.css +30 -0
  36. data/public/stylesheets/toolbox.css +107 -0
  37. data/view/toolbox/_collection.html.erb +24 -0
  38. data/view/toolbox/_collection_header.html.erb +7 -0
  39. data/view/toolbox/_context_menu.html.erb +17 -0
  40. data/view/toolbox/_dialogs.html.erb +6 -0
  41. data/view/toolbox/_form.html.erb +30 -0
  42. data/view/toolbox/_form_collection_row.html.erb +18 -0
  43. data/view/toolbox/_form_fieldset.html.erb +30 -0
  44. data/view/toolbox/_form_fieldset_row.html.erb +19 -0
  45. data/view/toolbox/_list.html.erb +25 -0
  46. data/view/toolbox/_list_row.html.erb +10 -0
  47. data/view/toolbox/_menu.html.erb +7 -0
  48. data/view/toolbox/_search_field.html.erb +8 -0
  49. data/view/toolbox/_show.html.erb +12 -0
  50. data/view/toolbox/_show_collection_row.html.erb +6 -0
  51. data/view/toolbox/_show_fieldset.html.erb +21 -0
  52. data/view/toolbox/edit.html.erb +5 -0
  53. data/view/toolbox/index.html.erb +3 -0
  54. data/view/toolbox/new.html.erb +9 -0
  55. data/view/toolbox/show.html.erb +39 -0
  56. metadata +178 -0
@@ -0,0 +1,413 @@
1
+ require File.dirname(__FILE__) << '/config'
2
+
3
+ # Helper method for form builders
4
+ module ActionView
5
+ module Helpers
6
+ class FormBuilder
7
+ def tag_id(method, options = {})
8
+ it = InstanceTag.new(@object_name, method, @template, nil, @object)
9
+ it.send :add_default_name_and_id, options #private method
10
+ options['id']
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ module Toolbox
17
+
18
+
19
+ class Renderer
20
+ attr_reader :widget_config
21
+
22
+ def initialize view, widget_config, dialog = false
23
+ @view = view
24
+ @widget_config = widget_config
25
+ @dialog = dialog
26
+ end
27
+
28
+
29
+ # Get the field value out of a model object
30
+ # If the attribute method is defined and it's a symbol, this method is called on the model object
31
+ # If the attribute method is defined and it's a block (Proc), the block is called with the model object as parameter
32
+ # In all other cases, the field name is interpreted as method and called on the model object
33
+ def value(rec)
34
+ val = rec.send(@widget_config.model_method) if @widget_config.model_method && @widget_config.model_method.is_a?(Symbol)
35
+ val = @widget_config.model_method.call(rec) if @widget_config.model_method && @widget_config.model_method.is_a?(Proc)
36
+ unless val # fallback
37
+ a = @widget_config.name.to_s.split('.')
38
+ a.each do |t|
39
+ rec = rec.send(t)
40
+ break unless rec # stop if nil is returned
41
+ end
42
+ val = rec
43
+ end
44
+ val = nil if val == ''
45
+ val
46
+ end
47
+
48
+ # Renders the label of the widget.
49
+ # If show_model is set to a model name, the model name of this widget
50
+ # will be added in parenthesis if it differs to the value in show_model.
51
+ def label(show_model = nil, html = true)
52
+ unless @widget_config.suppress_label
53
+ if @widget_config.label
54
+ html ? @view.send(:h, @widget_config.label) : @widget_config.label
55
+ else
56
+ translate_field(show_model, html)
57
+ end
58
+ end
59
+ end
60
+
61
+ # Translates a column name of a model using gettext
62
+ # The show_model argument is optional. If true, the model name will also be translated
63
+ # and appended to the column name. Ensure, that the model name is also translated
64
+ # (gettext does not extract this automatically).
65
+ #
66
+ def translate_field(show_model = nil, html = true)
67
+ col_name = @widget_config.name.to_s
68
+ model_name = @widget_config.model_name.classify
69
+ a = col_name.split('.')
70
+ if a.length > 1
71
+ model_name = a[-2].classify
72
+ col_name = a[-1]
73
+ end
74
+
75
+ text = @view.send(:s_, model_name + '|' + col_name.humanize)
76
+ text += ' (' + @view.send(:s_, model_name) + ')' if show_model && model_name != show_model
77
+
78
+ html ? @view.send(:h, text) : text
79
+ end
80
+
81
+ end
82
+
83
+ class FieldRenderer < Renderer
84
+ # Generate a sort link for the given field
85
+ # A list can only be sorted by one field.
86
+ # Multiple list on one page can be distinguished by the prefix
87
+ # The field parameter is a field form a list configuration (Symbol, String or Hash)
88
+ # The text is the link text.
89
+ #
90
+ # The following css classes are assigend regarding the current sort direction
91
+ # - no sorting: 'sort-none'
92
+ # - asc sorting: 'sort-asc'
93
+ # - desc sorting: 'sort-desc'
94
+ def sort_link(params, prefix = nil)
95
+ if @widget_config.suppress_sorting
96
+ label
97
+ else
98
+ # see if we are already sorted
99
+ current_dir = Toolbox::Sorting.current(params, prefix, @widget_config.name.to_s)
100
+ case current_dir
101
+ when nil
102
+ new_dir = 'asc'
103
+ class_attr = 'sort-none'
104
+ when 'asc'
105
+ new_dir = 'desc'
106
+ class_attr = 'sort-asc'
107
+ else
108
+ new_dir = nil
109
+ class_attr = 'sort-desc'
110
+ end
111
+ @view.link_to(label, Toolbox::Sorting.params(params, prefix, @widget_config.name.to_s, new_dir), {:class => class_attr})
112
+ end
113
+ end
114
+
115
+
116
+ # Renders the value of an attribute of a given object.
117
+ # Parameters:
118
+ # - object: the object to get the attribute of
119
+ # - html: if true, returns the value in html (escaped etc.)
120
+ # The following options are used:
121
+ # - :model_method => an alternative method or block of the object to get the display value
122
+ # - :render_method => an (helper/view) method or block that returns the rendered value (parameters: object, val, html)
123
+ # - :hide_empty: returns nil if the display value is empty. Otherwise, an empty string
124
+ # will be returned (or &nbsp; in case of html)
125
+ # - :suppress_link: if the display value is an ActiveRecord usually a link to it will
126
+ # be returned (only if html = true of course). If this option is set, only the string
127
+ # represenation will be returned
128
+ # - :join if the value is an array and :join is defined, the values are joined using the string
129
+ # representation of the :join value. Else, a unnumbered list (ul) is generated
130
+ #
131
+ # If the display value is an instance of Date, it will be formatted using the
132
+ # :local_date format
133
+ def render_value(object, html = true)
134
+ val = value(object)
135
+ unless val == nil && @widget_config.hide_empty
136
+ if @widget_config.render_method
137
+ render_own object, val, html
138
+ elsif val.is_a? Array
139
+ render_array object, val, html
140
+ else
141
+ value_text val, html, object
142
+ end
143
+ end
144
+ end
145
+
146
+ protected
147
+ # internal helper to get the text of a value-object
148
+ def value_text (val, html, context) #:nodoc:
149
+ text = val # default
150
+ unless val == nil && @widget_config.hide_empty
151
+ if val
152
+ if @widget_config.trunc && val.to_s.length > @widget_config.trunc
153
+ v = val.to_s
154
+ v = v[0,@widget_config.trunc] + '...'
155
+ text = html ? "<span title=\"#{@view.send(:h, val)}\">#{@view.send(:h, v)}</span>" : v
156
+ else
157
+ text = html ? @view.send(:h, val) : val
158
+ end
159
+ else
160
+ text = html ? '&nbsp;' : ''
161
+ end
162
+ if val.kind_of?(ActiveRecord::Base) && !@widget_config.suppress_link
163
+ if html
164
+ text = @view.send(:link_to, val.to_s, val)
165
+ text += @view.context_menu_link(val, context) unless @widget_config.suppress_context_menu
166
+ else
167
+ text = val.to_s
168
+ end
169
+ end
170
+ text = val.to_s(:local_date) if val.instance_of? Date
171
+ text = val.to_s(:local_time) if val.instance_of? Time
172
+ text = val.to_s(:local_datetime) if val.instance_of? ActiveSupport::TimeWithZone
173
+ text += @widget_config.suffix.to_s if @widget_config.suffix
174
+ end
175
+ text
176
+ end
177
+
178
+ private
179
+
180
+ def render_own (object, val, html)
181
+ if @widget_config.render_method.is_a? Proc
182
+ @widget_config.render_method.call @view, object, val, html
183
+ else
184
+ @view.send @widget_config.render_method, object, val, html
185
+ end
186
+ end
187
+
188
+ def render_array (object, val, html)
189
+ if val.size > 0
190
+ if @widget_config.join
191
+ val.map { |v| value_text(v, html, object) }.join(@widget_config.join.to_s)
192
+ else
193
+ @view.content_tag 'ul' do
194
+ val.map { |v| @view.content_tag 'li', value_text(v, html, object) }.join
195
+ end
196
+ end
197
+ else # map empty array to nil
198
+ value_text nil, html, object
199
+ end
200
+ end
201
+
202
+ end
203
+
204
+ class ControlRenderer < Renderer
205
+
206
+ def render_control(form, rec, has_error = false)
207
+ options = {}
208
+ options[:class] = 'error' if has_error
209
+ options[:class] = (options[:class] || '') + ' ' + @widget_config.css_class if @widget_config.css_class
210
+ options[:id] = "dialog_#{form.tag_id @widget_config.name}" if @dialog
211
+ options[:title] = @view.send(:h, @widget_config.info) if @widget_config.info
212
+ options[:disabled] = @widget_config.disabled if @widget_config.disabled and not @widget_config.disabled.is_a?(Proc)
213
+ options[:disabled] = @widget_config.disabled.call(rec) if @widget_config.disabled.is_a?(Proc)
214
+
215
+ case @widget_config.type
216
+ when :select
217
+ render_select form, rec, options
218
+ when :collection_select
219
+ render_collection_select form, rec, options
220
+ when :auto_complete
221
+ render_autocomplete form, rec, options
222
+ when :radio
223
+ render_radio form, rec, options
224
+ when :check_box
225
+ render_checkbox form, rec, options
226
+ when :date
227
+ render_date form, rec, options
228
+ when :textfield
229
+ render_textfield form, rec, options
230
+ when :textarea
231
+ render_textarea form, rec, options
232
+ when :filefield
233
+ render_filefield form, rec, options
234
+ when :hidden
235
+ render_hidden form, rec, options
236
+ else
237
+ raise "Unkown control type #{@widget_config.type}"
238
+ end
239
+ end
240
+
241
+ private
242
+
243
+ def render_select form, rec, options
244
+ form.select @widget_config.name, @widget_config.values, {}, options
245
+ end
246
+
247
+ def render_collection_select form, rec, options
248
+ form.collection_select @widget_config.name,
249
+ @widget_config.values,
250
+ @widget_config.model_method,
251
+ @widget_config.text_method,
252
+ {},
253
+ options.merge({ :multiple => true })
254
+ end
255
+
256
+ def render_autocomplete form, rec, options
257
+ t = rec.class.name.underscore
258
+ #id = "#{t}_#{@widget_config.name.to_s}"
259
+ id = options[:id] || form.tag_id(@widget_config.name)
260
+ name = @widget_config.name.to_s.foreign_key.to_sym
261
+ opt = {}
262
+ opt[:id] = "dialog_#{form.tag_id name}" if @dialog
263
+ s = form.hidden_field name, opt
264
+ s += form.text_field @widget_config.name, options.merge(:size => (@widget_config.size || 40))
265
+ s += @view.content_tag("div", "", :id => "#{id}_auto_complete", :class => "auto_complete")
266
+ s += @view.auto_complete_field(id,
267
+ :url => @view.send(@widget_config.url),
268
+ :method => :get,
269
+ :param_name => :autocomplete_query,
270
+ :select => 'autocomplete_name',
271
+ :after_update_element => 'auto_complete_update_hidden')
272
+ s
273
+ end
274
+
275
+ def render_radio form, rec, options
276
+ s = ''
277
+ @widget_config.values.each do |val|
278
+ s += form.radio_button @widget_config.name, val[1].to_s, options
279
+ s += @view.content_tag('span', val[0])
280
+ end
281
+ s
282
+ end
283
+
284
+ def render_checkbox form, rec, options
285
+ form.check_box @widget_config.name, options
286
+ end
287
+
288
+ def render_date form, rec, options
289
+ form.calendar_date_select @widget_config.name, options.merge(:size => (@widget_config.size || 15))
290
+ end
291
+
292
+ def render_textfield form, rec, options
293
+ val = value(rec) #rec.send @widget_config.name
294
+ text = val.to_s
295
+ text = val.to_s(:local_date) if val.instance_of? Date
296
+ text = val.to_s(:local_time) if val.instance_of? Time
297
+ text = val.to_s(:local_datetime) if val.instance_of? ActiveSupport::TimeWithZone
298
+ form.text_field @widget_config.name, options.merge({:size => (@widget_config.size || 40), :value => text})
299
+ end
300
+
301
+ def render_textarea form, rec, options
302
+ size = @widget_config.size || '40x10'
303
+ #val = rec.send @widget_config.name
304
+ #if val
305
+ # row = 0
306
+ # col = 0
307
+ # val.each_line do |line|
308
+ # row = row.succ
309
+ # col = [col, line.length].max
310
+ # end
311
+ # size = "#{col}x#{row}"
312
+ #end
313
+ form.text_area @widget_config.name, options.merge({:size => size})
314
+ end
315
+
316
+ def render_filefield form, rec, options
317
+ form.file_field @widget_config.name, options
318
+ end
319
+
320
+ def render_hidden form, rec, options
321
+ form.hidden_field @widget_config.name, options.merge({:value => value(rec)})
322
+ end
323
+
324
+ end
325
+
326
+ class ActionRenderer < Renderer
327
+
328
+ def label
329
+ if @widget_config.label
330
+ @widget_config.label
331
+ else
332
+ t = @widget_config.name.to_s.split '_'
333
+ l = @view.send(:_, t[0].humanize)
334
+ if t.size > 1 # e.g. new_group
335
+ model_name = t[1..-1].join '_'
336
+ l += ' ' + @view.send(:s_, model_name.humanize.downcase)
337
+ end
338
+ l
339
+ end
340
+ end
341
+
342
+ def render_action rec, context, current_action = nil
343
+ t = @widget_config.name.to_s.split '_'
344
+ action = t[0]
345
+ foreign_action = t.size > 1
346
+ if foreign_action # e.g. new_group
347
+ model_name = t[1..-1].join '_'
348
+ else
349
+ model_name = @widget_config.model_name
350
+ return nil if action == current_action
351
+ end
352
+ css_class = @widget_config.active == 1 ? 'enabled ' : 'disabled '
353
+ css_class += @widget_config.css_class || action
354
+
355
+ if @widget_config.active == 1
356
+ case action
357
+ when 'separator'
358
+ @widget_config.name if context
359
+ when 'new'
360
+ # copy the _id params to save the context
361
+ pars = {}
362
+ pars.merge! context if context
363
+ # delete self-reference
364
+ pars.delete model_name + '_id'
365
+ # set the object as parameter
366
+ pars[rec.class.name.underscore + '_id'] = rec #if foreign_action
367
+ url = @view.send("#{action}_#{model_name}_path".to_sym, pars)
368
+ if !@widget_config.direct_link && (context || foreign_action)
369
+ @view.link_to_remote label, { :url => url, :method => :get }, {:class => css_class }
370
+ else
371
+ @view.link_to label, url, {:class => css_class}
372
+ end
373
+ when 'show'
374
+ @view.link_to label, @view.polymorphic_path(rec), :class => css_class
375
+ when 'destroy'
376
+ msg = _('Are you sure?') #@view.send(:_, 'Are you sure?')
377
+ if context
378
+ url = @view.polymorphic_path(rec)
379
+ @view.link_to_remote label, { :url => url, :method => :delete, :confirm => msg }, {:class => css_class }
380
+ else
381
+ @view.link_to label, rec, :confirm => msg, :method => :delete, :class => css_class
382
+ end
383
+ when 'list'
384
+ @view.link_to label, @view.send("#{model_name.pluralize}_path".to_sym), :class => css_class
385
+ when 'edit'
386
+ url = @view.edit_polymorphic_path rec
387
+ if context
388
+ @view.link_to_remote label, { :url => url, :method => :get }, {:class => css_class }
389
+ else
390
+ @view.link_to label, url
391
+ end
392
+ else
393
+ url = @widget_config.url || @view.formatted_polymorphic_path([rec, action]) #eval("@view.#{action}_#{model_name}_path(rec)")
394
+ if context
395
+ if @widget_config.direct_link
396
+ @view.link_to label, url, :class => css_class
397
+ else
398
+ @view.link_to_remote label, { :url => url, :method => :get }, {:class => css_class }
399
+ end
400
+ else
401
+ @view.link_to label, url
402
+ end
403
+ end # case
404
+ else # if disabled
405
+ if context
406
+ @view.content_tag :a, label, :href => '#', :class => css_class
407
+ else
408
+ label
409
+ end
410
+ end
411
+ end
412
+ end
413
+ end
@@ -0,0 +1,85 @@
1
+ require 'gettext_rails'
2
+ require File.expand_path("../dirs", File.dirname(__FILE__))
3
+ require File.dirname(__FILE__) << '/config'
4
+ require File.dirname(__FILE__) << '/rendering'
5
+ module Toolbox
6
+ module Searching
7
+
8
+ def self.setup #:nodoc:
9
+ ActionController::Base.send(:extend, ControllerClassMethods)
10
+ ActionController::Base.send(:include, ControllerInstanceMethods)
11
+ ActionView::Base.send(:include, HelperMethods)
12
+ end
13
+
14
+ # Creates a SQL where condition with the given filter and a list of fields
15
+ # filter: the text to search for. Separated multiple value by space
16
+ # widgets_list: an instance of Toolbox::WidgetList. See ControllerClassMethods::search_fields
17
+ # for more information about the syntax.
18
+ # Example:
19
+ # search_field :firstname, :lastname, :address
20
+ # create_search_condition('dav ny')
21
+ # -> (firstname like 'dav' or lastname like 'dav' or address like 'dav')
22
+ # and (firstname like 'ny' or lastname like 'ny' or address like 'ny')
23
+ def self.create_search_condition(filter, widgets_list)
24
+ raise 'widgets_list are not of type Toolbox:WidgetList' unless widgets_list.is_a? Toolbox::WidgetList
25
+ cond_template = []
26
+ widgets_list.widgets.each do |w|
27
+ table = w.model_name.tableize
28
+ col_name = w.name
29
+ a = col_name.to_s.split('.')
30
+ if a.length > 1
31
+ table = a[-2].tableize
32
+ col_name = a[-1]
33
+ end
34
+ cond_template << "#{table}.#{col_name} like ? "
35
+ end
36
+ cond_template = '(' + cond_template.join(' or ') + ')'
37
+ cond = Array.new
38
+ cond.push('')
39
+ filter.squeeze(' ').split(' ').each do |text|
40
+ text = '%' + text + '%'
41
+ cond[0] += ' and ' if !cond[0].empty?
42
+ cond[0] += cond_template
43
+ cond_template.count('?').times do cond.push(text) end
44
+ end
45
+ # in case of empty search text
46
+ cond[0] = '1=1' if cond.length == 1
47
+ cond
48
+ end
49
+
50
+ module ControllerClassMethods
51
+ # Defines the search fields
52
+ # Example:
53
+ # search_field :firstname, :lastname, :address
54
+ # If symbols are given, the model is determined using the controllers name.
55
+ # If you want to search in associated tables, use this format:
56
+ # search_fields :firstname, :lastname, 'orders.desc'
57
+ def search_fields *fields
58
+ write_inheritable_attribute('search_fields', Toolbox::WidgetList.new(controller_name.singularize, fields, Toolbox::FieldConfig))
59
+ end
60
+
61
+ end
62
+
63
+ module ControllerInstanceMethods
64
+
65
+ # Returns the fields (array) defined with Toolbox::Searching::ControllerClassMethods::search_fields for this controller.
66
+ def search_fields
67
+ self.class.read_inheritable_attribute('search_fields') || nil
68
+ end
69
+
70
+ end
71
+
72
+ module HelperMethods
73
+ include GetText
74
+ bindtextdomain('toolbox', :path => Toolbox::Dirs::LOCALE)
75
+
76
+ def search_field_hint widgets_list
77
+ labels = []
78
+ widgets_list.widgets.each {|w| labels << Toolbox::Renderer.new(self, w).label(controller.model_name) }
79
+
80
+ _('Search in this fields:') + ' ' + labels.join(', ')
81
+ end
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,63 @@
1
+ require 'gettext_rails'
2
+
3
+ module Toolbox
4
+ module SessionParams
5
+
6
+ def self.setup #:nodoc:
7
+ ActionController::Base.send(:extend, ClassMethods)
8
+ ActionController::Base.send(:include, InstanceMethods)
9
+
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ # Makes the given parameters "persistend' (per controller).
15
+ # The listed parameters will be stored in the session.
16
+ # In a before filter, the params hash is set with the value from
17
+ # the session, if (and only if) the parameter does not exist in the session.
18
+ # Otherwise the session value is updated.
19
+ def session_params *params
20
+ write_inheritable_attribute('session_params', params)
21
+ end
22
+ end
23
+
24
+ module InstanceMethods #:nodoc:all
25
+
26
+ def self.included(base)
27
+ base.send :before_filter, :set_session_params
28
+ base.send :after_filter, :update_session_params
29
+ end
30
+
31
+ private
32
+
33
+ # update the parameter hash from the session
34
+ def set_session_params
35
+ if self.class.read_inheritable_attribute('session_params')
36
+ create_session_params
37
+ session[:session_params][controller_name].each_key do |field|
38
+ params[field] = session[:session_params][controller_name][field] unless params[field]
39
+ end
40
+ end
41
+ end
42
+ # update the session from the params hash
43
+ def update_session_params
44
+ if p = self.class.read_inheritable_attribute('session_params')
45
+ create_session_params
46
+ p.each do |field|
47
+ if params[field]
48
+ session[:session_params][controller_name][field] = params[field]
49
+ else
50
+ session[:session_params][controller_name].delete(field)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ def create_session_params
57
+ session[:session_params] = {} unless session[:session_params]
58
+ session[:session_params][controller_name] = {} unless session[:session_params][controller_name]
59
+ end
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,74 @@
1
+ require File.dirname(__FILE__) << '/config'
2
+ module Toolbox
3
+
4
+ module Sorting
5
+
6
+ def self.quote_order_by(table, field)
7
+ table = ActiveRecord::Base.connection.quote_table_name(table)
8
+ field = ActiveRecord::Base.connection.quote_column_name(field)
9
+ "#{table}.#{field}"
10
+ end
11
+
12
+ # Create an oder-by string
13
+ # Parameters
14
+ # - prefix: an optional prefix used to get the right sort-field from the params hash
15
+ # - params: the parameter hash
16
+ # - widgets: an array of field configuration
17
+ def self.order_by(params, widgets, prefix = nil)
18
+ val = params[params_key(prefix)]
19
+ return nil unless val # no sort parameter set
20
+ field, dir = val.split '-'
21
+ dir = fix_direction dir
22
+ return nil if dir == 'none' # no sort direction
23
+ # find the field in field list
24
+ field = widgets.select{ |w| w == field }.first
25
+ order_by_string = nil
26
+ if field
27
+ if field.order_by
28
+ order_by_string = field.order_by
29
+ order_by_string = order_by_string.join(" #{dir}, ") if order_by_string.is_a? Array
30
+ else
31
+ order_by_string = quote_order_by(field.model_name.tableize, field.name.to_s)
32
+ end
33
+ order_by_string += " #{dir}"
34
+ end
35
+ order_by_string
36
+ end
37
+
38
+ # inhibit any sql injection
39
+ def self.fix_direction dir #:nodoc:
40
+ dir == 'asc' ? 'asc' : (dir == 'desc' ? 'desc' : 'none')
41
+ end
42
+
43
+ # Get the current sorting direction from the given params hash
44
+ def self.current(params, prefix, field_name) #:nodoc:
45
+ dir = nil
46
+ if val = params[params_key(prefix)]
47
+ f, d = val.split '-'
48
+ dir = fix_direction(d) if f == field_name
49
+ end
50
+ dir
51
+ end
52
+
53
+ # Gets a parameter hash for link generation
54
+ def self.params(params, prefix, field_name, new_dir) #:nodoc:
55
+ if new_dir
56
+ params.merge({params_key(prefix) => "#{field_name}-#{new_dir}"})
57
+ else
58
+ params.delete params_key(prefix)
59
+ params
60
+ end
61
+ end
62
+
63
+ # Get the sort-key for the params hash regarding the prefix
64
+ def self.params_key(prefix) #:nodoc:
65
+ if prefix
66
+ "#{prefix}-sort".to_sym
67
+ else
68
+ :sort
69
+ end
70
+ end
71
+
72
+
73
+ end
74
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module Toolbox
3
+ VERSION = '0.1.0'
4
+ end
data/lib/toolbox.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'gettext'
2
+ require File.dirname(__FILE__) << '/toolbox/searching'
3
+ require File.dirname(__FILE__) << '/toolbox/sorting'
4
+ require File.dirname(__FILE__) << '/toolbox/session_params'
5
+ require File.dirname(__FILE__) << '/toolbox/helpers'
6
+ require File.dirname(__FILE__) << '/toolbox/default_controller'
7
+ require File.dirname(__FILE__) << '/toolbox/config'
8
+ require File.dirname(__FILE__) << '/toolbox/version'
9
+ require File.dirname(__FILE__) << '/dirs'
10
+
11
+ if Object.const_defined?(:Rails) && File.directory?(Rails.root.to_s + "/public")
12
+
13
+ Toolbox::Searching.setup
14
+ Toolbox::SessionParams.setup
15
+ Toolbox::DefaultController.setup
16
+ ActionController::Base.send(:append_view_path, Toolbox::Dirs::VIEW)
17
+
18
+ # install files
19
+ unless File.exists?(RAILS_ROOT + "/public/javascripts/toolbox-#{Toolbox::VERSION}/toolbox.js")
20
+ ['/public/javascripts', '/public/stylesheets', '/public/images'].each do |dir|
21
+ source = File.join(Toolbox::Dirs::GEM_ROOT, dir)
22
+ appendix = dir.ends_with?('javascripts') ? "toolbox-#{Toolbox::VERSION}" : 'toolbox'
23
+ dest = File.join(RAILS_ROOT, dir, appendix)
24
+ FileUtils.mkdir_p(dest)
25
+ FileUtils.cp(Dir.glob(source + '/*.*'), dest)
26
+ end
27
+ end
28
+ end
29
+
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file