vidl-toolbox 0.0.1
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/Rakefile +54 -0
- data/lib/dirs.rb +9 -0
- data/lib/toolbox/config.rb +185 -0
- data/lib/toolbox/default_controller.rb +315 -0
- data/lib/toolbox/helpers.rb +11 -0
- data/lib/toolbox/rendering.rb +305 -0
- data/lib/toolbox/searching.rb +85 -0
- data/lib/toolbox/session_params.rb +63 -0
- data/lib/toolbox/sorting.rb +73 -0
- data/lib/toolbox/version.rb +4 -0
- data/lib/toolbox.rb +29 -0
- data/locale/de/LC_MESSAGES/toolbox.mo +0 -0
- data/view/toolbox/_collection.html.erb +23 -0
- data/view/toolbox/_collection_header.html.erb +7 -0
- data/view/toolbox/_context_menu.html.erb +17 -0
- data/view/toolbox/_form.html.erb +28 -0
- data/view/toolbox/_form_collection_row.html.erb +16 -0
- data/view/toolbox/_form_fieldset.html.erb +19 -0
- data/view/toolbox/_list.html.erb +25 -0
- data/view/toolbox/_list_row.html.erb +13 -0
- data/view/toolbox/_menu.html.erb +7 -0
- data/view/toolbox/_search_field.html.erb +8 -0
- data/view/toolbox/_show.html.erb +11 -0
- data/view/toolbox/_show_collection_row.html.erb +6 -0
- data/view/toolbox/_show_fieldset.html.erb +21 -0
- data/view/toolbox/edit.html.erb +5 -0
- data/view/toolbox/index.html.erb +3 -0
- data/view/toolbox/new.html.erb +9 -0
- data/view/toolbox/show.html.erb +31 -0
- metadata +134 -0
@@ -0,0 +1,305 @@
|
|
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
|
+
# Renders the label of the widget.
|
30
|
+
# If show_model is set to a model name, the model name of this widget
|
31
|
+
# will be added in parenthesis if it differs to the value in show_model.
|
32
|
+
def label(show_model = nil)
|
33
|
+
@widget_config.label ? @view.send(:h, @widget_config.label) : translate_field(show_model) unless @widget_config.suppress_label
|
34
|
+
end
|
35
|
+
|
36
|
+
# Translates a column name of a model using gettext
|
37
|
+
# The show_model argument is optional. If true, the model name will also be translated
|
38
|
+
# and appended to the column name. Ensure, that the model name is also translated
|
39
|
+
# (gettext does not extract this automatically).
|
40
|
+
#
|
41
|
+
def translate_field(show_model = nil)
|
42
|
+
col_name = @widget_config.name.to_s
|
43
|
+
model_name = @widget_config.model_name.classify
|
44
|
+
a = col_name.split('.')
|
45
|
+
if a.length > 1
|
46
|
+
model_name = a[-2].classify
|
47
|
+
col_name = a[-1]
|
48
|
+
end
|
49
|
+
|
50
|
+
text = @view.send(:s_, model_name + '|' + col_name.humanize)
|
51
|
+
text += ' (' + @view.send(:s_, model_name) + ')' if show_model && model_name != show_model
|
52
|
+
@view.send(:h, text)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
class FieldRenderer < Renderer
|
58
|
+
# Generate a sort link for the given field
|
59
|
+
# A list can only be sorted by one field.
|
60
|
+
# Multiple list on one page can be distinguished by the prefix
|
61
|
+
# The field parameter is a field form a list configuration (Symbol, String or Hash)
|
62
|
+
# The text is the link text.
|
63
|
+
#
|
64
|
+
# The following css classes are assigend regarding the current sort direction
|
65
|
+
# - no sorting: 'sort-none'
|
66
|
+
# - asc sorting: 'sort-asc'
|
67
|
+
# - desc sorting: 'sort-desc'
|
68
|
+
def sort_link(params, prefix = nil)
|
69
|
+
if @widget_config.suppress_sorting
|
70
|
+
label
|
71
|
+
else
|
72
|
+
# see if we are already sorted
|
73
|
+
current_dir = Toolbox::Sorting.current(params, prefix, @widget_config.name.to_s)
|
74
|
+
case current_dir
|
75
|
+
when nil
|
76
|
+
new_dir = 'asc'
|
77
|
+
class_attr = 'sort-none'
|
78
|
+
when 'asc'
|
79
|
+
new_dir = 'desc'
|
80
|
+
class_attr = 'sort-asc'
|
81
|
+
else
|
82
|
+
new_dir = nil
|
83
|
+
class_attr = 'sort-desc'
|
84
|
+
end
|
85
|
+
@view.link_to(label, Toolbox::Sorting.params(params, prefix, @widget_config.name.to_s, new_dir), {:class => class_attr})
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Get the field value out of a model object
|
90
|
+
# If the attribute method is defined and it's a symbol, this method is called on the model object
|
91
|
+
# If the attribute method is defined and it's a block (Proc), the block is called with the model object as parameter
|
92
|
+
# In all other cases, the field name is interpreted as method and called on the model object
|
93
|
+
def value(rec)
|
94
|
+
val = rec.send(@widget_config.model_method) if @widget_config.model_method && @widget_config.model_method.is_a?(Symbol)
|
95
|
+
val = @widget_config.model_method.call(rec) if @widget_config.model_method && @widget_config.model_method.is_a?(Proc)
|
96
|
+
unless val # fallback
|
97
|
+
a = @widget_config.name.to_s.split('.')
|
98
|
+
a.each do |t|
|
99
|
+
rec = rec.send(t)
|
100
|
+
break unless rec # stop if nil is returned
|
101
|
+
end
|
102
|
+
val = rec
|
103
|
+
end
|
104
|
+
val
|
105
|
+
end
|
106
|
+
|
107
|
+
# Renders the value of an attribute of a given object.
|
108
|
+
# Parameters:
|
109
|
+
# - object: the object to get the attribute of
|
110
|
+
# - html: if true, returns the value in html (escaped etc.)
|
111
|
+
# The following options are used:
|
112
|
+
# - :model_method => an alternative method or block of the object to get the display value
|
113
|
+
# - :hide_empty: returns nil if the display value is empty. Otherwise, an empty string
|
114
|
+
# will be returned (or in case of html)
|
115
|
+
# - :suppress_link: if the display value is an ActiveRecord usually a link to it will
|
116
|
+
# be returned (only if html = true of course). If this option is set, only the string
|
117
|
+
# represenation will be returned
|
118
|
+
# - :join if the value is an array and :join is defined, the values are joined using the string
|
119
|
+
# representation of the :join value. Else, a unnumbered list (ul) is generated
|
120
|
+
#
|
121
|
+
# If the display value is an instance of Date, it will be formatted using the
|
122
|
+
# :local_date format
|
123
|
+
def render_value(object, html = true)
|
124
|
+
val = value(object)
|
125
|
+
if val.is_a? Array
|
126
|
+
if val.size > 0
|
127
|
+
if @widget_config.join
|
128
|
+
val.map { |v| value_text(v, html, object) }.join(@widget_config.join.to_s)
|
129
|
+
else
|
130
|
+
@view.content_tag 'ul' do
|
131
|
+
val.map { |v| @view.content_tag 'li', value_text(v, html, object) }.join
|
132
|
+
end
|
133
|
+
end
|
134
|
+
else # map empty array to nil
|
135
|
+
value_text nil, html, object
|
136
|
+
end
|
137
|
+
else # not an array
|
138
|
+
value_text val, html, object
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
protected
|
143
|
+
# internal helper to get the text of a value-object
|
144
|
+
def value_text (val, html, context) #:nodoc:
|
145
|
+
text = val # default
|
146
|
+
unless val == nil && @widget_config.hide_empty
|
147
|
+
if val
|
148
|
+
if @widget_config.trunc && val.to_s.length > @widget_config.trunc
|
149
|
+
v = val.to_s
|
150
|
+
v = v[0,@widget_config.trunc] + '...'
|
151
|
+
text = html ? "<span title=\"#{@view.send(:h, val)}\">#{@view.send(:h, v)}</span>" : v
|
152
|
+
else
|
153
|
+
text = html ? @view.send(:h, val) : val
|
154
|
+
end
|
155
|
+
else
|
156
|
+
text = html ? ' ' : ''
|
157
|
+
end
|
158
|
+
if val.kind_of?(ActiveRecord::Base) && !@widget_config.suppress_link
|
159
|
+
if html
|
160
|
+
text = @view.send(:link_to, val.to_s, val)
|
161
|
+
text += @view.context_menu_link(val, context) unless @widget_config.suppress_context_menu
|
162
|
+
else
|
163
|
+
text = val.to_s
|
164
|
+
end
|
165
|
+
end
|
166
|
+
text = val.to_s(:local_date) if val.instance_of? Date
|
167
|
+
text = val.to_s(:local_time) if val.instance_of? Time
|
168
|
+
text = val.to_s(:local_datetime) if val.instance_of? ActiveSupport::TimeWithZone
|
169
|
+
text += @widget_config.suffix.to_s if @widget_config.suffix
|
170
|
+
end
|
171
|
+
text
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
class ControlRenderer < Renderer
|
177
|
+
|
178
|
+
def render_control(form, rec, has_error = false)
|
179
|
+
options = {}
|
180
|
+
options[:class] = 'error' if has_error
|
181
|
+
options[:id] = "dialog_#{form.tag_id @widget_config.name}" if @dialog
|
182
|
+
case @widget_config.type
|
183
|
+
when :select
|
184
|
+
form.select @widget_config.name, @widget_config.values, {}, options
|
185
|
+
when :collection_select
|
186
|
+
form.collection_select @widget_config.name,
|
187
|
+
@widget_config.values,
|
188
|
+
@widget_config.model_method,
|
189
|
+
@widget_config.text_method,
|
190
|
+
{},
|
191
|
+
options.merge({ :multiple => true })
|
192
|
+
when :auto_complete
|
193
|
+
t = rec.class.name.underscore
|
194
|
+
#id = "#{t}_#{@widget_config.name.to_s}"
|
195
|
+
id = options[:id] || form.tag_id(@widget_config.name)
|
196
|
+
name = @widget_config.name.to_s.foreign_key.to_sym
|
197
|
+
opt = {}
|
198
|
+
opt[:id] = "dialog_#{form.tag_id name}" if @dialog
|
199
|
+
s = form.hidden_field name, opt
|
200
|
+
s += form.text_field @widget_config.name, options.merge(:size => (@widget_config.size || 40))
|
201
|
+
s += @view.content_tag("div", "", :id => "#{id}_auto_complete", :class => "auto_complete")
|
202
|
+
s += @view.auto_complete_field(id,
|
203
|
+
:url => @view.send(@widget_config.url),
|
204
|
+
:method => :get,
|
205
|
+
:param_name => :autocomplete_query,
|
206
|
+
:select => 'autocomplete_name',
|
207
|
+
:after_update_element => 'auto_complete_update_hidden'
|
208
|
+
)
|
209
|
+
s
|
210
|
+
when :radio
|
211
|
+
s = ''
|
212
|
+
@widget_config.values.each do |val|
|
213
|
+
s += form.radio_button @widget_config.name, val[1].to_s, options
|
214
|
+
s += @view.content_tag('span', val[0])
|
215
|
+
end
|
216
|
+
s
|
217
|
+
when :check_box
|
218
|
+
form.check_box @widget_config.name, options
|
219
|
+
when :date
|
220
|
+
form.calendar_date_select @widget_config.name, options.merge(:size => (@widget_config.size || 15))
|
221
|
+
when :textfield
|
222
|
+
val = rec.send @widget_config.name
|
223
|
+
text = val.to_s
|
224
|
+
text = val.to_s(:local_date) if val.instance_of? Date
|
225
|
+
text = val.to_s(:local_time) if val.instance_of? Time
|
226
|
+
text = val.to_s(:local_datetime) if val.instance_of? ActiveSupport::TimeWithZone
|
227
|
+
form.text_field @widget_config.name, options.merge({:size => (@widget_config.size || 40), :value => text})
|
228
|
+
else
|
229
|
+
raise "Unkown control type #{@widget_config.type}"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
class ActionRenderer < Renderer
|
236
|
+
|
237
|
+
def label
|
238
|
+
if @widget_config.label
|
239
|
+
@widget_config.label
|
240
|
+
else
|
241
|
+
t = @widget_config.name.to_s.split '_'
|
242
|
+
l = @view.send(:_, t[0].humanize)
|
243
|
+
if t.size > 1 # e.g. new_group
|
244
|
+
model_name = t[1..-1].join '_'
|
245
|
+
l += ' ' + @view.send(:s_, model_name.humanize.downcase)
|
246
|
+
end
|
247
|
+
l
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def render_action rec, context, current_action = nil
|
252
|
+
t = @widget_config.name.to_s.split '_'
|
253
|
+
action = t[0]
|
254
|
+
foreign_action = t.size > 1
|
255
|
+
if foreign_action # e.g. new_group
|
256
|
+
model_name = t[1..-1].join '_'
|
257
|
+
else
|
258
|
+
model_name = @widget_config.model_name
|
259
|
+
return nil if action == current_action
|
260
|
+
end
|
261
|
+
css_class = "enabled #{action}"
|
262
|
+
|
263
|
+
case action
|
264
|
+
when 'separator'
|
265
|
+
@widget_config.name if context
|
266
|
+
when 'new'
|
267
|
+
# copy the _id params to save the context
|
268
|
+
pars = {}
|
269
|
+
pars.merge! context if context
|
270
|
+
# delete self-reference
|
271
|
+
pars.delete model_name + '_id'
|
272
|
+
# set the object as parameter
|
273
|
+
pars[rec.class.name.underscore + '_id'] = rec #if foreign_action
|
274
|
+
url = @view.send("#{action}_#{model_name}_path".to_sym, pars)
|
275
|
+
if context || foreign_action
|
276
|
+
@view.link_to_remote label, { :url => url, :method => :get }, {:class => css_class }
|
277
|
+
else
|
278
|
+
@view.link_to label, url, {:class => css_class}
|
279
|
+
end
|
280
|
+
when 'show'
|
281
|
+
@view.link_to label, @view.polymorphic_path(rec), :class => css_class
|
282
|
+
when 'destroy'
|
283
|
+
msg = @view.send :_, 'Are you sure?'
|
284
|
+
if context
|
285
|
+
url = @view.polymorphic_path(rec)
|
286
|
+
@view.link_to_remote label, { :url => url, :method => :delete, :confirm => msg }, {:class => css_class }
|
287
|
+
else
|
288
|
+
@view.link_to label, rec, :confirm => msg, :method => :delete, :class => css_class
|
289
|
+
end
|
290
|
+
when 'list'
|
291
|
+
@view.link_to label, @view.send("#{model_name.pluralize}_path".to_sym), :class => css_class
|
292
|
+
when 'edit'
|
293
|
+
url = @view.edit_polymorphic_path rec
|
294
|
+
if context
|
295
|
+
@view.link_to_remote label, { :url => url, :method => :get }, {:class => css_class }
|
296
|
+
else
|
297
|
+
@view.link_to label, url
|
298
|
+
end
|
299
|
+
else
|
300
|
+
url = eval("@view.#{action}_#{model_name}_path(rec)")
|
301
|
+
@view.link_to_remote label, { :url => url, :method => :get }, {:class => css_class }
|
302
|
+
end # case
|
303
|
+
end
|
304
|
+
end
|
305
|
+
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::Rails
|
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,73 @@
|
|
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
|
+
else
|
30
|
+
order_by_string = quote_order_by(field.model_name.tableize, field.name.to_s)
|
31
|
+
end
|
32
|
+
order_by_string += " #{dir}"
|
33
|
+
end
|
34
|
+
order_by_string
|
35
|
+
end
|
36
|
+
|
37
|
+
# inhibit any sql injection
|
38
|
+
def self.fix_direction dir #:nodoc:
|
39
|
+
dir == 'asc' ? 'asc' : (dir == 'desc' ? 'desc' : 'none')
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get the current sorting direction from the given params hash
|
43
|
+
def self.current(params, prefix, field_name) #:nodoc:
|
44
|
+
dir = nil
|
45
|
+
if val = params[params_key(prefix)]
|
46
|
+
f, d = val.split '-'
|
47
|
+
dir = fix_direction(d) if f == field_name
|
48
|
+
end
|
49
|
+
dir
|
50
|
+
end
|
51
|
+
|
52
|
+
# Gets a parameter hash for link generation
|
53
|
+
def self.params(params, prefix, field_name, new_dir) #:nodoc:
|
54
|
+
if new_dir
|
55
|
+
params.merge({params_key(prefix) => "#{field_name}-#{new_dir}"})
|
56
|
+
else
|
57
|
+
params.delete params_key(prefix)
|
58
|
+
params
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get the sort-key for the params hash regarding the prefix
|
63
|
+
def self.params_key(prefix) #:nodoc:
|
64
|
+
if prefix
|
65
|
+
"#{prefix}-sort".to_sym
|
66
|
+
else
|
67
|
+
:sort
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
end
|
73
|
+
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
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<%
|
2
|
+
no_form = local_assigns[:form].nil?
|
3
|
+
rec = collection # just for more readable code
|
4
|
+
collection = rec.send(widgetset.collection_config.model_method)
|
5
|
+
locals = {:parent => rec, :widgetset => widgetset, :renderers => renderers}
|
6
|
+
id = widgetset.collection_config.object_id.to_s + '_' + dom_id(rec)
|
7
|
+
unless no_form and collection.empty? and widgetset.collection_config.hide_empty
|
8
|
+
field_set_tag widgetset.label do %>
|
9
|
+
<table id="<%= id %>">
|
10
|
+
<%= render :partial => 'toolbox/collection_header', :locals => locals %>
|
11
|
+
<%= render :partial => no_form ? 'toolbox/show_collection_row' : 'toolbox/form_collection_row', :collection => collection, :locals => locals %>
|
12
|
+
</table>
|
13
|
+
<% unless no_form %>
|
14
|
+
<%=
|
15
|
+
|
16
|
+
link_to_function image_tag('toolbox/add.png', :border => 0) do |page|
|
17
|
+
page.insert_html :bottom, id, :partial => 'toolbox/form_collection_row', :object => widgetset.collection_config.type.new, :locals => locals.merge({:auto_id => true})
|
18
|
+
end
|
19
|
+
%>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
23
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<%
|
2
|
+
rec = context_menu
|
3
|
+
renderer = widget_list.widgets.map { |a| Toolbox::ActionRenderer.new self, a }
|
4
|
+
%>
|
5
|
+
<ul>
|
6
|
+
<li style="color: darkgray; text-align:center;"><%= s_(rec.class.name.underscore.humanize.downcase) %></li>
|
7
|
+
<li class="separator"></li>
|
8
|
+
<% renderer.each do |r|
|
9
|
+
a = r.render_action rec, context
|
10
|
+
if a.is_a? String
|
11
|
+
%>
|
12
|
+
<li><%= a %></li>
|
13
|
+
<% else %>
|
14
|
+
<li class="<%= a.to_s %>"></li>
|
15
|
+
<% end %>
|
16
|
+
<% end %>
|
17
|
+
</ul>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<% # Aufruf: render :partial => "toolbox/form", :object => @article, :locals => { :fsets => ..., :options => ... } %>
|
2
|
+
<%
|
3
|
+
rec = form # just for more readable code
|
4
|
+
dialog = false unless dialog
|
5
|
+
form_method = dialog ? :remote_form_for : :form_for
|
6
|
+
|
7
|
+
%>
|
8
|
+
<% send(form_method, rec) do |frm| %>
|
9
|
+
<%= frm.error_messages %>
|
10
|
+
<% widgetsets.each do |widgetset|
|
11
|
+
renderers = widgetset.widget_list.widgets.map { |w| Toolbox::ControlRenderer.new(self, w, dialog) }
|
12
|
+
if widgetset.condition == nil || widgetset.condition.call(rec) %>
|
13
|
+
<%= render :partial => widgetset.collection_config ? 'toolbox/collection': 'toolbox/form_fieldset',
|
14
|
+
:object => rec,
|
15
|
+
:locals => {:form => frm, :widgetset => widgetset, :renderers => renderers} %>
|
16
|
+
<% end %>
|
17
|
+
<% end %>
|
18
|
+
<div class="toolbar">
|
19
|
+
<%= frm.submit(rec.new_record?() ? Toolbox::Messages.create : Toolbox::Messages.save, :name => :save) %>
|
20
|
+
<% if dialog %>
|
21
|
+
<%= button_to_function Toolbox::Messages.cancel, "$('dialog_id').popup.hide()" %>
|
22
|
+
<% else %>
|
23
|
+
<%= frm.submit Toolbox::Messages.cancel, :name => :cancel %>
|
24
|
+
<% end %>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<%
|
2
|
+
rec = form_collection_row # just for more readable code
|
3
|
+
parent_object_name = ActionController::RecordIdentifier.singular_class_name(parent)
|
4
|
+
collection_attribute_name = Toolbox::Helpers.collection_attribute_name(widgetset.collection_config, rec)
|
5
|
+
prefix = "#{parent_object_name}[#{collection_attribute_name}][]"
|
6
|
+
has_error = parent.errors.on(widgetset.collection_config.model_method)
|
7
|
+
%>
|
8
|
+
<% fields_for prefix, rec do |frm| %>
|
9
|
+
<tr>
|
10
|
+
<% renderers.each do |control_renderer| %>
|
11
|
+
<td><%= control_renderer.render_control(frm, rec, has_error) %></td>
|
12
|
+
<% end %>
|
13
|
+
<td><%= link_to_function image_tag('toolbox/remove.png', :border => 0), "$(this).up('tr').remove()" %>
|
14
|
+
</tr>
|
15
|
+
<% end %>
|
16
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<% # renders a fieldset with a normal label: value structure %>
|
2
|
+
<%
|
3
|
+
rec = form_fieldset # just for more readable code
|
4
|
+
frm = form
|
5
|
+
field_set_tag widgetset.label do
|
6
|
+
%>
|
7
|
+
<table border="0">
|
8
|
+
<%
|
9
|
+
renderers.each do |renderer|
|
10
|
+
has_error = rec.errors.on(renderer.widget_config.name)
|
11
|
+
%>
|
12
|
+
<tr>
|
13
|
+
<td><%= frm.label renderer.widget_config.name unless renderer.widget_config.suppress_label %></td>
|
14
|
+
<td ><%= renderer.render_control(frm, rec, has_error) %></td>
|
15
|
+
</tr>
|
16
|
+
<% end %>
|
17
|
+
</table>
|
18
|
+
<% end %>
|
19
|
+
|