netzke-basepack 0.4.2 → 0.5.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/.autotest +1 -0
- data/.gitignore +6 -0
- data/{CHANGELOG → CHANGELOG.rdoc} +26 -0
- data/README.rdoc +11 -11
- data/Rakefile +37 -11
- data/TODO.rdoc +8 -0
- data/VERSION +1 -0
- data/javascripts/basepack.js +71 -28
- data/lib/app/models/netzke_auto_column.rb +56 -0
- data/lib/netzke-basepack.rb +5 -3
- data/lib/netzke/accordion_panel.rb +69 -67
- data/lib/netzke/active_record/basepack.rb +104 -0
- data/lib/netzke/active_record/data_accessor.rb +33 -0
- data/lib/netzke/basic_app.rb +233 -124
- data/lib/netzke/border_layout_panel.rb +97 -98
- data/lib/netzke/configuration_panel.rb +24 -0
- data/lib/netzke/data_accessor.rb +71 -0
- data/lib/netzke/ext.rb +6 -0
- data/lib/netzke/field_model.rb +1 -1
- data/lib/netzke/fields_configurator.rb +62 -37
- data/lib/netzke/form_panel.rb +161 -51
- data/lib/netzke/form_panel_api.rb +74 -0
- data/lib/netzke/form_panel_js.rb +129 -0
- data/lib/netzke/grid_panel.rb +385 -80
- data/lib/netzke/grid_panel_api.rb +352 -0
- data/lib/netzke/grid_panel_extras/javascripts/rows-dd.js +280 -0
- data/lib/netzke/grid_panel_js.rb +721 -0
- data/lib/netzke/masquerade_selector.rb +53 -0
- data/lib/netzke/panel.rb +9 -0
- data/lib/netzke/plugins/configuration_tool.rb +121 -0
- data/lib/netzke/property_editor.rb +95 -7
- data/lib/netzke/property_editor_extras/helper_model.rb +55 -34
- data/lib/netzke/search_panel.rb +62 -0
- data/lib/netzke/tab_panel.rb +97 -37
- data/lib/netzke/table_editor.rb +49 -44
- data/lib/netzke/tree_panel.rb +15 -16
- data/lib/netzke/wrapper.rb +29 -5
- data/netzke-basepack.gemspec +151 -19
- data/stylesheets/basepack.css +5 -0
- data/test/app_root/app/models/book.rb +1 -1
- data/test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb +1 -1
- data/test/unit/accordion_panel_test.rb +1 -2
- data/test/unit/active_record_basepack_test.rb +54 -0
- data/test/unit/grid_panel_test.rb +8 -12
- data/test/unit/helper_model_test.rb +30 -0
- metadata +69 -78
- data/Manifest +0 -86
- data/TODO +0 -3
- data/lib/app/models/netzke_hash_record.rb +0 -180
- data/lib/app/models/netzke_layout_item.rb +0 -11
- data/lib/netzke/ar_ext.rb +0 -269
- data/lib/netzke/configuration_tool.rb +0 -80
- data/lib/netzke/container.rb +0 -77
- data/lib/netzke/db_fields.rb +0 -44
- data/lib/netzke/fields_configurator_old.rb +0 -62
- data/lib/netzke/form_panel_extras/interface.rb +0 -56
- data/lib/netzke/form_panel_extras/js_builder.rb +0 -134
- data/lib/netzke/grid_panel_extras/interface.rb +0 -206
- data/lib/netzke/grid_panel_extras/js_builder.rb +0 -352
- data/test/unit/ar_ext_test.rb +0 -53
- data/test/unit/netzke_hash_record_test.rb +0 -52
- data/test/unit/netzke_layout_item_test.rb +0 -28
data/lib/netzke/grid_panel.rb
CHANGED
@@ -1,135 +1,440 @@
|
|
1
1
|
require 'searchlogic'
|
2
2
|
|
3
3
|
module Netzke
|
4
|
+
# == GridPanel
|
5
|
+
# Ext.grid.EditorGridPanel + server-side code
|
4
6
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# *
|
9
|
-
# * column resize and move
|
10
|
-
# * column hide - TODO
|
7
|
+
# == Features:
|
8
|
+
# * multi-line CRUD operations - get, post, delete, create
|
9
|
+
# * (multe-record) editing and adding records through a form
|
10
|
+
# * column resize, move and hide
|
11
11
|
# * permissions
|
12
12
|
# * sorting
|
13
13
|
# * pagination
|
14
14
|
# * filtering
|
15
|
-
# *
|
15
|
+
# * extended configurable search
|
16
|
+
# * rows reordering (drag-n-drop)
|
17
|
+
# * dynamic configuration of properties and columns
|
16
18
|
#
|
19
|
+
# == Class configuration
|
20
|
+
# Configuration on this level is effective during the life-time of the application. They can be put into a .rb file
|
21
|
+
# inside of config/initializers like this:
|
22
|
+
#
|
23
|
+
# Netzke::GridPanel.configure :column_filters_available, false
|
24
|
+
# Netzke::GridPanel.configure :default_config => {:ext_config => {:enable_config_tool => false}}
|
25
|
+
#
|
26
|
+
# Most of these options directly influence the amount of JavaScript code that is generated for this widget's class.
|
27
|
+
# The less functionality is enabled, the less code is generated.
|
28
|
+
#
|
29
|
+
# The following configuration options are available:
|
30
|
+
# * <tt>:column_filters_available</tt> - (default is true) include code for the filters in the column's context menu
|
31
|
+
# * <tt>:config_tool_available</tt> - (default is true) include code for the configuration tool that launches the configuration panel
|
32
|
+
# * <tt>:edit_in_form_available</tt> - (defaults to true) include code for (multi-record) editing and adding records through a form
|
33
|
+
# * <tt>:extended_search_available</tt> - (defaults to true) include code for extended configurable search
|
34
|
+
# * <tt>:default_config</tt> - a hash of default configuration options for each instance of the GridPanel widget.
|
35
|
+
# See the "Instance configuration" section below.
|
36
|
+
#
|
37
|
+
# == Instance configuration
|
38
|
+
# The following config options are available:
|
39
|
+
# * <tt>:data_class_name</tt> - name of the ActiveRecord model that provides data to this GridPanel.
|
40
|
+
# * <tt>:strong_default_attrs</tt> - a hash of attributes to be merged atop of every created/updated record.
|
41
|
+
# * <tt>:scopes</tt> - an array of searchlogic-compatible scopes to filter grid data like this:
|
42
|
+
#
|
43
|
+
# ["user_id_not", 100]
|
44
|
+
#
|
45
|
+
# In the <tt>:ext_config</tt> hash (see Netzke::Base) the following GridPanel specific options are available:
|
46
|
+
#
|
47
|
+
# * <tt>:enable_column_filters</tt> - enable filters in column's context menu
|
48
|
+
# * <tt>:enable_edit_in_form</tt> - provide buttons into the toolbar that activate editing/adding records via a form
|
49
|
+
# * <tt>:enable_extended_search</tt> - provide a button into the toolbar that shows configurable search form
|
50
|
+
# * <tt>:enable_context_menu</tt> - enable rows context menu
|
51
|
+
# * <tt>:enable_rows_reordering</tt> - enable reordering of rows with drag-n-drop; underlying model (specified in <tt>:data_class_name</tt>) must implement "acts_as_list"-compatible functionality; defaults to <tt>false</tt>
|
52
|
+
# * <tt>:enable_pagination</tt> - enable pagination; defaults to <tt>true</tt>
|
53
|
+
# * <tt>:rows_per_page</tt> - number of rows per page (ignored when <tt>:enable_pagination</tt> is set to <tt>false</tt>)
|
54
|
+
# * <tt>:load_inline_data</tt> - load initial data into the grid right after its instantiation (saves a request to server); defaults to <tt>true</tt>
|
55
|
+
# * <tt>:mode</tt> - when set to <tt>:config</tt>, GridPanel loads in configuration mode
|
56
|
+
#
|
57
|
+
# Additionally supports Netzke::Base config options.
|
17
58
|
class GridPanel < Base
|
18
|
-
|
59
|
+
# javascript (client-side)
|
60
|
+
include Netzke::GridPanelJs
|
61
|
+
# API (server-side)
|
62
|
+
include Netzke::GridPanelApi
|
63
|
+
# Code shared between GridPanel, FormPanel, and other widgets that serve as interface to database tables
|
64
|
+
include Netzke::DataAccessor
|
65
|
+
|
66
|
+
def self.enforce_config_consistency
|
67
|
+
config[:default_config][:ext_config][:enable_edit_in_form] &&= config[:edit_in_form_available]
|
68
|
+
config[:default_config][:ext_config][:enable_extended_search] &&= config[:extended_search_available]
|
69
|
+
config[:default_config][:ext_config][:enable_rows_reordering] &&= config[:rows_reordering_available]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Class-level configuration. This options directly influence the amount of generated
|
73
|
+
# javascript code for this widget's class. For example, if you don't want filters for the grid,
|
74
|
+
# set :column_filters_available to false, and the javascript for the filters won't be included at all.
|
19
75
|
def self.config
|
20
76
|
set_default_config({
|
21
|
-
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
|
77
|
+
|
78
|
+
:column_filters_available => true,
|
79
|
+
:config_tool_available => true,
|
80
|
+
:edit_in_form_available => true,
|
81
|
+
:extended_search_available => true,
|
82
|
+
:rows_reordering_available => false,
|
83
|
+
|
84
|
+
:default_config => {
|
85
|
+
:ext_config => {
|
86
|
+
:enable_edit_in_form => true,
|
87
|
+
:enable_extended_search => true,
|
88
|
+
:enable_column_filters => true,
|
89
|
+
:load_inline_data => true,
|
90
|
+
:enable_context_menu => true,
|
91
|
+
|
92
|
+
:enable_pagination => true,
|
93
|
+
:rows_per_page => 25,
|
94
|
+
|
95
|
+
:mode => :normal, # when set to :config, :configuration button is enabled
|
96
|
+
|
97
|
+
:enable_rows_reordering => false, # drag n drop
|
98
|
+
|
99
|
+
:tools => %w{ refresh }
|
100
|
+
},
|
101
|
+
|
102
|
+
:persistent_config => false
|
103
|
+
}
|
28
104
|
})
|
29
105
|
end
|
30
106
|
|
31
|
-
|
32
|
-
|
33
|
-
|
107
|
+
# Include extra javascript that we depend on
|
108
|
+
def self.include_js
|
109
|
+
res = []
|
110
|
+
|
111
|
+
# Checkcolumn
|
112
|
+
res << "#{File.dirname(__FILE__)}/grid_panel_extras/javascripts/check-column.js"
|
113
|
+
|
114
|
+
# Filters
|
115
|
+
if config[:column_filters_available]
|
116
|
+
ext_examples = Netzke::Base.config[:ext_location] + "/examples/"
|
117
|
+
res << ext_examples + "grid-filtering/menu/EditableItem.js"
|
118
|
+
res << ext_examples + "grid-filtering/menu/RangeMenu.js"
|
119
|
+
res << ext_examples + "grid-filtering/grid/GridFilters.js"
|
34
120
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
121
|
+
%w{Boolean Date List Numeric String}.unshift("").each do |f|
|
122
|
+
res << ext_examples + "grid-filtering/grid/filter/#{f}Filter.js"
|
123
|
+
end
|
124
|
+
|
125
|
+
res << "#{File.dirname(__FILE__)}/grid_panel_extras/javascripts/filters.js"
|
126
|
+
|
127
|
+
end
|
40
128
|
|
41
|
-
|
129
|
+
# DD
|
130
|
+
if config[:rows_reordering_available]
|
131
|
+
res << "#{File.dirname(__FILE__)}/grid_panel_extras/javascripts/rows-dd.js"
|
132
|
+
end
|
133
|
+
|
134
|
+
res
|
42
135
|
end
|
136
|
+
|
137
|
+
# Define connection points between client side and server side of GridPanel.
|
138
|
+
# See implementation of equally named methods in the GridPanelApi module.
|
139
|
+
api :get_data, :post_data, :delete_data, :resize_column, :move_column, :hide_column, :get_combobox_options, :move_rows
|
43
140
|
|
44
|
-
#
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
interface :get_data, :post_data, :delete_data, :resize_column, :move_column, :hide_column, :get_cb_choices
|
50
|
-
|
51
|
-
# widget type for DbFields
|
52
|
-
# TODO: ugly, rethink
|
53
|
-
def self.widget_type
|
54
|
-
:grid
|
55
|
-
end
|
56
|
-
|
57
|
-
# default instance-level configuration
|
58
|
-
def initial_config
|
59
|
-
{
|
60
|
-
:ext_config => {
|
61
|
-
:config_tool => self.class.config[:config_tool_enabled_by_default],
|
62
|
-
:enable_column_filters => self.class.config[:enable_filters],
|
63
|
-
:enable_column_move => self.class.config[:column_move_enabled_by_default],
|
64
|
-
:enable_column_hide => self.class.config[:column_hide_enabled_by_default],
|
65
|
-
:enable_column_resize => self.class.config[:column_resize_enabled_by_default]
|
66
|
-
},
|
67
|
-
:persistent_layout => self.class.config[:persistent_layout_enabled_by_default],
|
68
|
-
:persistent_config => self.class.config[:persistent_config_enabled_by_default]
|
69
|
-
}
|
141
|
+
# Edit in form
|
142
|
+
api :create_new_record if config[:edit_in_form_available]
|
143
|
+
|
144
|
+
def data_class
|
145
|
+
@data_class ||= config[:data_class_name].nil? ? raise(ArgumentError, "No data_class_name specified for widget #{id_name}") : config[:data_class_name].constantize
|
70
146
|
end
|
71
147
|
|
72
|
-
|
73
|
-
|
148
|
+
|
149
|
+
def initialize(config = {}, parent = nil)
|
150
|
+
super
|
151
|
+
|
152
|
+
apply_helpers
|
74
153
|
end
|
75
154
|
|
155
|
+
# Columns to be displayed by the FieldConfigurator.
|
156
|
+
def self.config_columns
|
157
|
+
[
|
158
|
+
{:name => :name, :type => :string, :editor => :combobox, :width => 200},
|
159
|
+
{:name => :excluded, :type => :boolean, :editor => :checkbox, :width => 40, :header => "Excl"},
|
160
|
+
{:name => :value},
|
161
|
+
{:name => :header},
|
162
|
+
{:name => :hidden, :type => :boolean, :editor => :checkbox},
|
163
|
+
{:name => :read_only, :type => :boolean, :editor => :checkbox, :header => "R"},
|
164
|
+
{:name => :editor, :type => :string, :editor => {:xtype => :combobox, :options => Netzke::Ext::FORM_FIELD_XTYPES}},
|
165
|
+
{:name => :renderer, :type => :string},
|
166
|
+
# {:name => :renderer, :type => :string, :editor => {:xtype => :jsonfield}},
|
167
|
+
{:name => :with_filters, :type => :boolean, :editor => :checkbox, :default => true, :header => "Filters"},
|
168
|
+
|
169
|
+
# some rarely used configurations, hidden
|
170
|
+
{:name => :width, :type => :integer, :editor => :numberfield, :hidden => true},
|
171
|
+
{:name => :hideable, :type => :boolean, :editor => :checkbox, :default => true, :hidden => true},
|
172
|
+
{:name => :sortable, :type => :boolean, :editor => :checkbox, :default => true, :hidden => true},
|
173
|
+
]
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.property_fields
|
177
|
+
res = [
|
178
|
+
{:name => :ext_config__title, :type => :string},
|
179
|
+
{:name => :ext_config__header, :type => :boolean, :default => true},
|
180
|
+
{:name => :ext_config__enable_context_menu, :type => :boolean, :default => true},
|
181
|
+
{:name => :ext_config__context_menu, :type => :json},
|
182
|
+
{:name => :ext_config__enable_pagination, :type => :boolean, :default => true},
|
183
|
+
{:name => :ext_config__rows_per_page, :type => :integer},
|
184
|
+
# {:name => :ext_config__bbar, :type => :json},
|
185
|
+
{:name => :ext_config__prohibit_create, :type => :boolean},
|
186
|
+
{:name => :ext_config__prohibit_update, :type => :boolean},
|
187
|
+
{:name => :ext_config__prohibit_delete, :type => :boolean},
|
188
|
+
{:name => :ext_config__prohibit_read, :type => :boolean}
|
189
|
+
]
|
190
|
+
|
191
|
+
res << {:name => :ext_config__enable_extended_search, :type => :boolean} if config[:extended_search_available]
|
192
|
+
res << {:name => :ext_config__enable_edit_in_form, :type => :boolean} if config[:edit_in_form_available]
|
193
|
+
|
194
|
+
res
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
def independent_config
|
199
|
+
res = super
|
200
|
+
|
201
|
+
# Bottom bar
|
202
|
+
if res[:ext_config][:bbar].nil?
|
203
|
+
res[:ext_config][:bbar] = %w{ add edit apply del }
|
204
|
+
res[:ext_config][:bbar] << "-" << "add_in_form" << "edit_in_form" if res[:ext_config][:enable_edit_in_form]
|
205
|
+
res[:ext_config][:bbar] << "-" << "search" if res[:ext_config][:enable_extended_search]
|
206
|
+
end
|
207
|
+
|
208
|
+
# Context menu
|
209
|
+
res[:ext_config][:context_menu] ||= default_context_menu(res)
|
210
|
+
|
211
|
+
res
|
212
|
+
end
|
213
|
+
|
214
|
+
def default_context_menu(indep_config)
|
215
|
+
res = %w{ edit del }
|
216
|
+
res << "-" << "edit_in_form" if indep_config[:ext_config][:enable_edit_in_form]
|
217
|
+
res
|
218
|
+
end
|
219
|
+
|
76
220
|
def configuration_widgets
|
77
221
|
res = []
|
78
222
|
res << {
|
223
|
+
:persistent_config => true,
|
79
224
|
:name => 'columns',
|
80
225
|
:widget_class_name => "FieldsConfigurator",
|
81
226
|
:active => true,
|
82
|
-
:widget => self
|
83
|
-
|
84
|
-
} if config[:persistent_layout]
|
85
|
-
|
227
|
+
:widget => self
|
228
|
+
}
|
86
229
|
res << {
|
87
230
|
:name => 'general',
|
88
231
|
:widget_class_name => "PropertyEditor",
|
89
|
-
:
|
232
|
+
:widget => self,
|
90
233
|
:ext_config => {:title => false}
|
91
234
|
}
|
92
|
-
|
93
235
|
res
|
94
236
|
end
|
95
237
|
|
96
|
-
def tools
|
97
|
-
%w{ refresh }
|
98
|
-
end
|
99
|
-
|
100
238
|
def actions
|
101
|
-
|
102
|
-
|
103
|
-
:
|
104
|
-
:
|
239
|
+
# Defaults
|
240
|
+
res = {
|
241
|
+
:add => {:text => 'Add', :disabled => ext_config[:prohibit_create]},
|
242
|
+
:edit => {:text => 'Edit', :disabled => true},
|
243
|
+
:del => {:text => 'Delete', :disabled => true},
|
244
|
+
:apply => {:text => 'Apply', :disabled => ext_config[:prohibit_update] && ext_config[:prohibit_create]}
|
105
245
|
}
|
246
|
+
|
247
|
+
# Edit in form
|
248
|
+
res.merge!({
|
249
|
+
:add_in_form => {:text => 'Add in form'},
|
250
|
+
:edit_in_form => {:text => 'Edit in form', :disabled => true}
|
251
|
+
}) if ext_config[:enable_edit_in_form]
|
252
|
+
|
253
|
+
# Extended search
|
254
|
+
res.merge!({
|
255
|
+
:search => {:text => 'Search'}
|
256
|
+
}) if ext_config[:enable_extended_search]
|
257
|
+
|
258
|
+
res
|
106
259
|
end
|
107
260
|
|
108
|
-
def
|
109
|
-
|
261
|
+
def initial_late_aggregatees
|
262
|
+
res = {}
|
263
|
+
|
264
|
+
# Edit in form
|
265
|
+
res.merge!({
|
266
|
+
:edit_form => {
|
267
|
+
:widget_class_name => "FormPanel",
|
268
|
+
:persistent_config => true,
|
269
|
+
:data_class_name => config[:data_class_name],
|
270
|
+
:ext_config => {
|
271
|
+
:bbar => false,
|
272
|
+
:header => false,
|
273
|
+
:mode => ext_config[:mode]
|
274
|
+
}
|
275
|
+
},
|
276
|
+
|
277
|
+
:multi_edit_form => {
|
278
|
+
:widget_class_name => "FormPanel",
|
279
|
+
:persistent_config => true,
|
280
|
+
:data_class_name => config[:data_class_name],
|
281
|
+
:ext_config => {
|
282
|
+
:bbar => false,
|
283
|
+
:header => false,
|
284
|
+
:mode => ext_config[:mode]
|
285
|
+
}
|
286
|
+
},
|
287
|
+
|
288
|
+
:new_record_form => {
|
289
|
+
:widget_class_name => "FormPanel",
|
290
|
+
:persistent_config => true,
|
291
|
+
:data_class_name => config[:data_class_name],
|
292
|
+
:ext_config => {
|
293
|
+
:bbar => false,
|
294
|
+
:header => false,
|
295
|
+
:mode => ext_config[:mode]
|
296
|
+
},
|
297
|
+
:record => config[:data_class_name].constantize.new
|
298
|
+
}
|
299
|
+
}) if ext_config[:enable_edit_in_form]
|
300
|
+
|
301
|
+
# Extended search
|
302
|
+
res.merge!({
|
303
|
+
:search_panel => {
|
304
|
+
:widget_class_name => "SearchPanel",
|
305
|
+
:search_class_name => config[:data_class_name],
|
306
|
+
:persistent_config => true,
|
307
|
+
:ext_config => {
|
308
|
+
:header => false,
|
309
|
+
:bbar => false,
|
310
|
+
:mode => ext_config[:mode]
|
311
|
+
},
|
312
|
+
}
|
313
|
+
}) if ext_config[:enable_extended_search]
|
314
|
+
|
315
|
+
res
|
110
316
|
end
|
111
317
|
|
318
|
+
|
319
|
+
include Plugins::ConfigurationTool if config[:config_tool_available] # it will load ConfigurationPanel into a modal window
|
320
|
+
|
112
321
|
def columns
|
113
322
|
@columns ||= get_columns
|
114
323
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
def available_permissions
|
121
|
-
%w(read update create delete)
|
324
|
+
|
325
|
+
# Normalized columns
|
326
|
+
def normalized_columns
|
327
|
+
@normalized_columns ||= normalize_columns(columns)
|
122
328
|
end
|
123
329
|
|
124
330
|
def get_columns
|
125
|
-
if config[:
|
126
|
-
|
127
|
-
|
128
|
-
|
331
|
+
if config[:persistent_config]
|
332
|
+
persistent_config['layout__columns'] ||= default_columns
|
333
|
+
res = normalize_array_of_columns(persistent_config['layout__columns'])
|
334
|
+
else
|
335
|
+
res = default_columns
|
336
|
+
end
|
337
|
+
|
338
|
+
# denormalize
|
339
|
+
res.map{ |c| c.is_a?(Hash) && c.reject{ |k,v| k == :name }.empty? ? c[:name].to_sym : c }
|
340
|
+
end
|
341
|
+
|
342
|
+
# Normalizes the column at position +index+ and returns it.
|
343
|
+
def column_at(index)
|
344
|
+
if columns[index].is_a?(Hash)
|
345
|
+
columns[index]
|
129
346
|
else
|
130
|
-
|
347
|
+
column_name = columns.delete_at(index)
|
348
|
+
normalized_column = normalize_column(column_name)
|
349
|
+
columns.insert(index, normalized_column)
|
350
|
+
normalized_column
|
131
351
|
end
|
132
352
|
end
|
133
353
|
|
354
|
+
# Stores modified columns in persistent storage
|
355
|
+
def save_columns!
|
356
|
+
persistent_config[:layout__columns] = columns
|
357
|
+
end
|
358
|
+
|
359
|
+
TYPE_EDITOR_MAP = {
|
360
|
+
:integer => :numberfield,
|
361
|
+
:boolean => :checkbox,
|
362
|
+
:date => :datefield,
|
363
|
+
:datetime => :xdatetime,
|
364
|
+
:text => :textarea
|
365
|
+
# :string => :textfield
|
366
|
+
}
|
367
|
+
|
368
|
+
def default_columns
|
369
|
+
# columns specified in widget's config
|
370
|
+
columns_from_config = config[:columns] && normalize_columns(config[:columns])
|
371
|
+
|
372
|
+
if columns_from_config
|
373
|
+
# reverse-merge each column hash from config with each column hash from exposed_attributes (columns from config have higher priority)
|
374
|
+
for c in columns_from_config
|
375
|
+
corresponding_exposed_column = predefined_columns.find{ |k| k[:name] == c[:name] }
|
376
|
+
c.reverse_merge!(corresponding_exposed_column) if corresponding_exposed_column
|
377
|
+
end
|
378
|
+
columns_for_create = columns_from_config
|
379
|
+
else
|
380
|
+
# we didn't have columns configured in widget's config, so, use the columns from the data class
|
381
|
+
columns_for_create = predefined_columns
|
382
|
+
end
|
383
|
+
|
384
|
+
columns_for_create.map! do |c|
|
385
|
+
# detect ActiveRecord column type (if the column is "real") or fall back to :virtual
|
386
|
+
type = (data_class.columns_hash[c[:name].to_s] && data_class.columns_hash[c[:name].to_s].type) || :virtual
|
387
|
+
|
388
|
+
# detect :assoc__method columns
|
389
|
+
if c[:name].to_s.index('__')
|
390
|
+
assoc_name, method = c[:name].to_s.split('__').map(&:to_sym)
|
391
|
+
if assoc = data_class.reflect_on_association(assoc_name)
|
392
|
+
assoc_column = assoc.klass.columns_hash[method.to_s]
|
393
|
+
assoc_method_type = assoc_column.try(:type)
|
394
|
+
if assoc_method_type
|
395
|
+
c[:editor] ||= TYPE_EDITOR_MAP[assoc_method_type] == :checkbox ? :checkbox : :combobox
|
396
|
+
end
|
397
|
+
type = :association
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
# detect association column (e.g. :category_id)
|
402
|
+
assoc = data_class.reflect_on_all_associations.detect{|a| a.primary_key_name.to_sym == c[:name]}
|
403
|
+
if assoc && !assoc.options[:polymorphic]
|
404
|
+
c[:editor] ||= :combobox
|
405
|
+
assoc_method = %w{name title label id}.detect{|m| (assoc.klass.instance_methods + assoc.klass.column_names).include?(m) } || assoc.klass.primary_key
|
406
|
+
c[:name] = "#{assoc.name}__#{assoc_method}".to_sym
|
407
|
+
type = :association
|
408
|
+
end
|
409
|
+
|
410
|
+
# Some smart defaults
|
411
|
+
|
412
|
+
# default editor, dependent on column type
|
413
|
+
c[:editor] ||= TYPE_EDITOR_MAP[type] unless TYPE_EDITOR_MAP[type].nil?
|
414
|
+
# narrow column for checkbox
|
415
|
+
c[:width] ||= 50 if c[:editor] == :checkbox
|
416
|
+
# wider column for xdatetime
|
417
|
+
c[:width] ||= 120 if c[:editor] == :xdatetime
|
418
|
+
# hide ID column
|
419
|
+
c[:hidden] = true if c[:name] == data_class.primary_key.to_sym && c[:hidden].nil?
|
420
|
+
|
421
|
+
# Some default limitations for virtual columns
|
422
|
+
if type == :virtual
|
423
|
+
# disable filters
|
424
|
+
c[:with_filters].nil? && c[:with_filters] = false
|
425
|
+
# disable sorting
|
426
|
+
c[:sortable].nil? && c[:sortable] = false
|
427
|
+
# read-only
|
428
|
+
c[:read_only].nil? && c[:read_only] = true
|
429
|
+
end
|
430
|
+
|
431
|
+
# denormalize column (save space)
|
432
|
+
c.reject{ |k,v| k == :name }.empty? ? c[:name] : c
|
433
|
+
end
|
434
|
+
|
435
|
+
columns_for_create
|
436
|
+
|
437
|
+
end
|
438
|
+
|
134
439
|
end
|
135
440
|
end
|