netzke-basepack 0.5.8 → 0.5.9
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/CHANGELOG.rdoc +18 -0
- data/README.rdoc +11 -4
- data/Rakefile +19 -3
- data/TODO.rdoc +1 -1
- data/generators/netzke_basepack/netzke_basepack_generator.rb +13 -0
- data/generators/netzke_basepack/templates/create_netzke_field_lists.rb +18 -0
- data/generators/netzke_basepack/templates/public_assets/ts-checkbox.gif +0 -0
- data/install.rb +1 -1
- data/javascripts/basepack.js +124 -30
- data/lib/app/models/netzke_field_list.rb +261 -0
- data/lib/app/models/netzke_model_attr_list.rb +21 -0
- data/lib/app/models/netzke_persistent_array_auto_model.rb +58 -0
- data/lib/netzke-basepack.rb +17 -2
- data/lib/netzke/active_record.rb +10 -0
- data/lib/netzke/active_record/association_attributes.rb +102 -0
- data/lib/netzke/active_record/attributes.rb +100 -0
- data/lib/netzke/active_record/combobox_options.rb +43 -0
- data/lib/netzke/active_record/data_accessor.rb +9 -12
- data/lib/netzke/attributes_configurator.rb +195 -0
- data/lib/netzke/basic_app.rb +47 -4
- data/lib/netzke/configuration_panel.rb +1 -1
- data/lib/netzke/data_accessor.rb +7 -30
- data/lib/netzke/fields_configurator.rb +106 -41
- data/lib/netzke/form_panel.rb +28 -125
- data/lib/netzke/form_panel/form_panel_api.rb +2 -3
- data/lib/netzke/form_panel/form_panel_fields.rb +147 -0
- data/lib/netzke/form_panel/form_panel_js.rb +35 -15
- data/lib/netzke/grid_panel.rb +130 -213
- data/lib/netzke/grid_panel/grid_panel_api.rb +254 -257
- data/lib/netzke/grid_panel/grid_panel_columns.rb +226 -0
- data/lib/netzke/grid_panel/grid_panel_js.rb +126 -119
- data/lib/netzke/grid_panel/record_form_window.rb +7 -1
- data/lib/netzke/json_array_editor.rb +61 -0
- data/lib/netzke/plugins/configuration_tool.rb +1 -1
- data/lib/netzke/property_editor.rb +3 -3
- data/lib/netzke/search_panel.rb +164 -27
- data/lib/netzke/tab_panel.rb +14 -12
- data/stylesheets/basepack.css +43 -2
- data/test/app_root/app/models/book.rb +1 -1
- data/test/app_root/app/models/role.rb +3 -0
- data/test/app_root/app/models/user.rb +3 -0
- data/test/app_root/config/database.yml +1 -1
- data/test/app_root/db/migrate/20090102223630_create_netzke_field_lists.rb +18 -0
- data/test/app_root/db/migrate/20090423214303_create_roles.rb +11 -0
- data/test/app_root/db/migrate/20090423222114_create_users.rb +12 -0
- data/test/fixtures/books.yml +4 -2
- data/test/fixtures/categories.yml +2 -2
- data/test/fixtures/genres.yml +6 -6
- data/test/fixtures/roles.yml +8 -0
- data/test/fixtures/users.yml +11 -0
- data/test/test_helper.rb +2 -0
- data/test/unit/active_record_basepack_test.rb +2 -2
- data/test/unit/fields_configuration_test.rb +18 -0
- data/test/unit/grid_panel_test.rb +29 -27
- metadata +41 -16
- data/lib/app/models/netzke_auto_column.rb +0 -4
- data/lib/app/models/netzke_auto_field.rb +0 -4
- data/lib/app/models/netzke_auto_table.rb +0 -61
- data/lib/netzke/active_record/basepack.rb +0 -134
- data/lib/netzke/grid_panel/javascripts/filters.js +0 -7
- data/test/app_root/db/migrate/20090102223630_create_netzke_layouts.rb +0 -14
- data/test/unit/helper_model_test.rb +0 -30
@@ -0,0 +1,61 @@
|
|
1
|
+
module Netzke
|
2
|
+
# Abstract GridPanel-based editor for a JSON array of homogenious objects.
|
3
|
+
# Inherit from it in order to override:
|
4
|
+
# <tt>store_data</p> - passes the data to be saved (e.g. to the persistant storage)
|
5
|
+
# <tt>initial_data</p> - should return initial data (e.g. from the persistant storage)
|
6
|
+
# For an example of an implementation, see Netzke::FieldsConfigurator.
|
7
|
+
class JsonArrayEditor < GridPanel
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
data_class.configure(:owner => global_id, :columns => dynamic_fields, :initial_data => initial_data)
|
11
|
+
end
|
12
|
+
|
13
|
+
def data_class
|
14
|
+
NetzkePersistentArrayAutoModel
|
15
|
+
end
|
16
|
+
|
17
|
+
# Fields for NetzkePersistentArrayAutoModel (override it)
|
18
|
+
def dynamic_fields
|
19
|
+
default_columns.collect { |c| {:name => c[:name], :type => c[:attr_type], :default => c[:default_value]} }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Default predifined columns (override if needed)
|
23
|
+
def default_columns
|
24
|
+
[{
|
25
|
+
:name => :id,
|
26
|
+
:attr_type => :integer
|
27
|
+
},{
|
28
|
+
:name => :name,
|
29
|
+
:attr_type => :string
|
30
|
+
},{
|
31
|
+
:name => :position,
|
32
|
+
:attr_type => :integer
|
33
|
+
}]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Don't show the config tool
|
37
|
+
# def config_tool_needed?
|
38
|
+
# false
|
39
|
+
# end
|
40
|
+
|
41
|
+
def before_load
|
42
|
+
data_class.rebuild_table
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
# Override this
|
48
|
+
def store_data(data); end
|
49
|
+
|
50
|
+
# Override this
|
51
|
+
def initial_data
|
52
|
+
[]
|
53
|
+
end
|
54
|
+
|
55
|
+
# This is an override of GridPanel#on_data_changed
|
56
|
+
def on_data_changed
|
57
|
+
store_data(data_class.all_columns)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -22,7 +22,7 @@ module Netzke::Plugins
|
|
22
22
|
|
23
23
|
# if you include ConfigurationTool, you are supposed to provide configuration_widgets method which will returns an array of arrgeratees
|
24
24
|
# that will be included in the property window (each in its own tab or accordion pane)
|
25
|
-
raise "configuration_widgets method undefined" unless base.instance_methods.include?(
|
25
|
+
raise "configuration_widgets method undefined" unless base.instance_methods.map(&:to_sym).include?(:configuration_widgets)
|
26
26
|
end
|
27
27
|
|
28
28
|
module ClassMethods
|
@@ -16,12 +16,12 @@ module Netzke
|
|
16
16
|
{:restore_defaults => {:text => "Restore defaults"}}
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def fields
|
20
20
|
fields = @widget.class.property_fields
|
21
21
|
|
22
22
|
for f in fields
|
23
23
|
f[:value] = @widget.flat_config(f[:name]).nil? ? f[:default] : @widget.flat_config(f[:name])
|
24
|
-
f[:xtype] =
|
24
|
+
f[:xtype] = xtype_for_attr_type(f[:type])
|
25
25
|
f[:field_label] = f[:name].to_s.gsub("__", "/").humanize
|
26
26
|
end
|
27
27
|
|
@@ -95,7 +95,7 @@ module Netzke
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def normalize_form_value(value, field)
|
98
|
-
case field[:
|
98
|
+
case field[:attr_type]
|
99
99
|
when :boolean
|
100
100
|
value.to_b
|
101
101
|
when :json
|
data/lib/netzke/search_panel.rb
CHANGED
@@ -5,7 +5,8 @@ module Netzke
|
|
5
5
|
# Pretty much work in progress.
|
6
6
|
class SearchPanel < FormPanel
|
7
7
|
|
8
|
-
|
8
|
+
# Something like [:equals, :greater_than_or_equal_to, :does_not_equal, :less_than, :less_than_or_equal_to, :greater_than, :ends_with, :like, :begins_with, :empty, :null]
|
9
|
+
CONDITIONS = [:COMPARISON_CONDITIONS, :WILDCARD_CONDITIONS, :BOOLEAN_CONDITIONS].inject([]){|r, c| r + Searchlogic::NamedScopes::Conditions.const_get(c).keys}
|
9
10
|
|
10
11
|
def default_config
|
11
12
|
super.merge({
|
@@ -13,50 +14,186 @@ module Netzke
|
|
13
14
|
})
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
17
|
+
def independent_config
|
18
|
+
super.deep_merge(
|
19
|
+
:ext_config => {
|
20
|
+
:tbar => ["Presets:",
|
21
|
+
{
|
22
|
+
:xtype => "combo",
|
23
|
+
:fieldLabel => "Presets",
|
24
|
+
:triggerAction => "all",
|
25
|
+
:store => (persistent_config[:saved_searches] || []).map{ |s| s["name"] },
|
26
|
+
:id => "presets-combo",
|
27
|
+
:listeners => {:before_select => {
|
28
|
+
:fn => "function(combo, record){Ext.getCmp('#{global_id}').selectPreset(record.data.field1);}".l
|
29
|
+
}}
|
30
|
+
}, :save, :del]
|
31
|
+
}
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def actions
|
36
|
+
super.merge(
|
37
|
+
:save => {:text => "Save", :icon => "/images/icons/disk.png"},
|
38
|
+
:del => {:text => "Delete", :icon => "/images/icons/delete.png"}
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.js_extend_properties
|
43
|
+
{
|
44
|
+
:remove_search_from_list => <<-END_OF_JAVASCRIPT.l,
|
45
|
+
function(name){
|
46
|
+
var presetsCombo = Ext.getCmp("presets-combo");
|
47
|
+
var presetsComboStore = presetsCombo.getStore();
|
48
|
+
presetsComboStore.removeAt(presetsComboStore.find('field1', name));
|
49
|
+
presetsCombo.reset();
|
50
|
+
this.getForm().reset();
|
51
|
+
}
|
52
|
+
END_OF_JAVASCRIPT
|
53
|
+
|
54
|
+
:set_values => <<-END_OF_JAVASCRIPT.l,
|
55
|
+
function(values){
|
56
|
+
this.getForm().setValues(Ext.decode(values));
|
57
|
+
}
|
58
|
+
END_OF_JAVASCRIPT
|
25
59
|
|
26
|
-
|
60
|
+
:select_preset => <<-END_OF_JAVASCRIPT.l,
|
61
|
+
function(name){
|
62
|
+
this.getForm().reset();
|
63
|
+
this.loadSearch({name: name});
|
64
|
+
}
|
65
|
+
END_OF_JAVASCRIPT
|
66
|
+
|
67
|
+
:on_save => <<-END_OF_JAVASCRIPT.l,
|
68
|
+
function(){
|
69
|
+
var searchName = Ext.getCmp("presets-combo").getValue();
|
70
|
+
if (searchName !== "") {
|
71
|
+
var presetsComboStore = Ext.getCmp("presets-combo").getStore();
|
72
|
+
if (presetsComboStore.find('field1', searchName) !== -1) {
|
73
|
+
Ext.Msg.confirm("Overwriting preset '" + searchName + "'", "Are you sure you want to overwrite this preset?", function(btn, text){
|
74
|
+
if (btn == 'yes') {
|
75
|
+
this.doSavePreset(searchName);
|
76
|
+
}
|
77
|
+
}, this);
|
78
|
+
} else {
|
79
|
+
this.doSavePreset(searchName);
|
80
|
+
presetsComboStore.add(new presetsComboStore.recordType({field1: searchName}));
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
END_OF_JAVASCRIPT
|
85
|
+
|
86
|
+
:do_save_preset => <<-END_OF_JAVASCRIPT.l,
|
87
|
+
function(name){
|
88
|
+
var values = this.getForm().getValues();
|
89
|
+
for (var k in values) {
|
90
|
+
if (values[k] == "") {delete values[k]}
|
91
|
+
}
|
92
|
+
|
93
|
+
this.saveSearch({
|
94
|
+
name: name,
|
95
|
+
values: Ext.encode(values)
|
96
|
+
});
|
97
|
+
}
|
98
|
+
END_OF_JAVASCRIPT
|
99
|
+
|
100
|
+
:on_del => <<-END_OF_JAVASCRIPT.l,
|
101
|
+
function(){
|
102
|
+
var searchName = Ext.getCmp("presets-combo").getValue();
|
103
|
+
if (searchName !== "") {
|
104
|
+
Ext.Msg.confirm("Deleting preset '" + searchName + "'", "Are you sure you want to delete this preset?", function(btn, text){
|
105
|
+
if (btn == 'yes') {
|
106
|
+
this.deleteSearch({
|
107
|
+
name: searchName
|
108
|
+
});
|
109
|
+
}
|
110
|
+
}, this);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
END_OF_JAVASCRIPT
|
114
|
+
|
115
|
+
}
|
116
|
+
end
|
117
|
+
|
118
|
+
api :save_search
|
119
|
+
def save_search(params)
|
120
|
+
saved_searches = persistent_config[:saved_searches] || []
|
121
|
+
existing = saved_searches.detect{ |s| s["name"] == params[:name] }
|
122
|
+
values = ActiveSupport::JSON.decode(params[:values])
|
123
|
+
if existing
|
124
|
+
existing["values"].replace(values)
|
125
|
+
else
|
126
|
+
saved_searches << {"name" => params[:name], "values" => values}
|
27
127
|
end
|
128
|
+
persistent_config[:saved_searches] = saved_searches
|
129
|
+
{:feedback => "Preset successfully saved"}
|
130
|
+
end
|
131
|
+
|
132
|
+
api :delete_search
|
133
|
+
def delete_search(params)
|
134
|
+
saved_searches = persistent_config[:saved_searches]
|
135
|
+
saved_searches.delete_if{ |s| s["name"] == params[:name] }
|
136
|
+
{:feedback => "Preset successfully deleted", :remove_search_from_list => params[:name]}
|
137
|
+
end
|
138
|
+
|
139
|
+
api :load_search
|
140
|
+
def load_search(params)
|
141
|
+
saved_searches = persistent_config[:saved_searches]
|
142
|
+
the_search = saved_searches.detect{ |s| s["name"] == params[:name] }
|
143
|
+
|
144
|
+
{:set_values => the_search["values"].to_json}
|
145
|
+
end
|
146
|
+
|
147
|
+
def initial_fields(only_included = true)
|
148
|
+
res = super
|
149
|
+
|
150
|
+
res.reject!{ |f| f[:virtual] }
|
28
151
|
|
152
|
+
res.each do |f|
|
153
|
+
f.merge!(:condition => "like", :default_value => nil)
|
154
|
+
f.merge!(:xtype => xtype_for_attr_type(:string), :attr_type => "string") if f[:name].to_s.index("__")
|
155
|
+
f.merge!(:condition => "greater_than") if [:datetime, :integer, :date].include?(f[:attr_type].to_sym)
|
156
|
+
f.merge!(:condition => "equals") if f["attr_type"] == "boolean"
|
157
|
+
end
|
158
|
+
|
29
159
|
res
|
30
160
|
end
|
31
161
|
|
32
162
|
# columns to be displayed by the FieldConfigurator (which is GridPanel-based)
|
33
|
-
def self.
|
34
|
-
[
|
35
|
-
{:name =>
|
36
|
-
{:name =>
|
37
|
-
{:name =>
|
38
|
-
{:name =>
|
39
|
-
{:name =>
|
40
|
-
{:name =>
|
163
|
+
def self.meta_columns
|
164
|
+
[
|
165
|
+
{:name => "hidden", :attr_type => :boolean, :editor => :checkbox, :width => 50},
|
166
|
+
{:name => "name", :attr_type => :string, :editor => :combobox},
|
167
|
+
{:name => "condition", :attr_type => :string, :editor => {:xtype => :combo, :store => CONDITIONS}},
|
168
|
+
{:name => "field_label", :attr_type => :string},
|
169
|
+
{:name => "xtype", :attr_type => :string},
|
170
|
+
{:name => "value", :attr_type => :string},
|
41
171
|
]
|
42
172
|
end
|
43
173
|
|
44
174
|
# tweaking the form fields at the last moment
|
45
175
|
def js_config
|
46
176
|
super.merge({
|
47
|
-
:
|
48
|
-
:
|
177
|
+
:fields => fields.map{ |c| c.merge({
|
178
|
+
:label => "#{c[:label] || c[:name]} #{c[:condition]}".humanize,
|
49
179
|
:name => "#{c[:name]}_#{c[:condition]}"
|
50
180
|
})}
|
51
181
|
})
|
52
182
|
end
|
53
183
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
184
|
+
private
|
185
|
+
# we need to correct the queries to cut off the condition suffixes, otherwise the FormPanel gets confused
|
186
|
+
def get_combobox_options(params)
|
187
|
+
column_name = params[:column]
|
188
|
+
CONDITIONS.each { |c| column_name.sub!(/_#{c}$/, "") }
|
189
|
+
super(:column => column_name)
|
190
|
+
end
|
191
|
+
|
192
|
+
def attr_type_to_xtype_map
|
193
|
+
super.merge({
|
194
|
+
:boolean => :tricheckbox
|
195
|
+
})
|
196
|
+
end
|
60
197
|
|
61
198
|
end
|
62
199
|
end
|
data/lib/netzke/tab_panel.rb
CHANGED
@@ -28,23 +28,25 @@ module Netzke
|
|
28
28
|
|
29
29
|
// We do this all in +render+ because only at this moment the activeTab is actually activated
|
30
30
|
var activeTab = this.getActiveTab();
|
31
|
-
|
31
|
+
|
32
|
+
// Insert (render) preloaded widgets into their respective tabs
|
33
|
+
this.items.each(function(fitPanel){
|
34
|
+
var preloadedItemConfig = this[fitPanel.widget.camelize(true)+"Config"];
|
35
|
+
if (preloadedItemConfig){
|
36
|
+
var klass = this.classifyScopedName(preloadedItemConfig.scopedClassName);
|
37
|
+
fitPanel.add(new klass(preloadedItemConfig));
|
38
|
+
}
|
39
|
+
}, this);
|
40
|
+
|
41
|
+
// setting the tabchange event
|
32
42
|
this.on('tabchange', this.onTabChange, this);
|
33
43
|
}
|
34
44
|
END_OF_JAVASCRIPT
|
35
45
|
|
46
|
+
# loads widget from the server
|
36
47
|
:load_widget_into => <<-END_OF_JAVASCRIPT.l,
|
37
48
|
function(fitPanel){
|
38
|
-
|
39
|
-
if (preloadedItemConfig){
|
40
|
-
// preloaded widget only needs to be instantiated, as its class and configuration have already been loaded
|
41
|
-
var klass = this.classifyScopedName(preloadedItemConfig.scopedClassName);
|
42
|
-
fitPanel.add(new klass(preloadedItemConfig));
|
43
|
-
fitPanel.doLayout();
|
44
|
-
} else {
|
45
|
-
// load the widget from the server
|
46
|
-
this.loadAggregatee({id:fitPanel.widget, container:fitPanel.id});
|
47
|
-
}
|
49
|
+
this.loadAggregatee({id:fitPanel.widget, container:fitPanel.id});
|
48
50
|
}
|
49
51
|
END_OF_JAVASCRIPT
|
50
52
|
|
@@ -78,7 +80,7 @@ module Netzke
|
|
78
80
|
|
79
81
|
:on_tab_change => <<-END_OF_JAVASCRIPT.l
|
80
82
|
function(self, tab) {
|
81
|
-
// load widget into the panel if it
|
83
|
+
// load widget into the panel from the server if it's not there yet
|
82
84
|
if (!tab.getWidget()) {
|
83
85
|
this.loadWidgetInto(tab);
|
84
86
|
}
|
data/stylesheets/basepack.css
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/* Fix for Ext's EditableItem render problem (from http://www.extjs.com/forum/showthread.php?p=290267#post290267) */
|
2
|
-
|
2
|
+
/*.x-menu div.x-menu-item .x-menu-item-icon {
|
3
3
|
left: 0px;
|
4
4
|
margin-top: 0px !important;
|
5
5
|
position:relative;
|
@@ -14,7 +14,7 @@ left: 3px;
|
|
14
14
|
.ext-ie6 .x-menu-item-icon {
|
15
15
|
left: -24px;
|
16
16
|
}
|
17
|
-
|
17
|
+
*/
|
18
18
|
/* write accordion header in bold */
|
19
19
|
.x-accordion-hd {
|
20
20
|
font-weight:bold;
|
@@ -28,4 +28,45 @@ left: -24px;
|
|
28
28
|
/* StatusBar - fixing forgotten "!important" */
|
29
29
|
.x-statusbar .x-status-busy {
|
30
30
|
padding-left: 25px !important;
|
31
|
+
}
|
32
|
+
|
33
|
+
/* Tri-state checkbox */
|
34
|
+
.x-form-tscheckbox {
|
35
|
+
height: 13px;
|
36
|
+
width: 13px;
|
37
|
+
background: url('basepack/ts-checkbox.gif') no-repeat 0 0;
|
38
|
+
vertical-align: bottom;
|
39
|
+
}
|
40
|
+
.x-checkbox-checked .x-form-tscheckbox {
|
41
|
+
background-position:0 -13px;
|
42
|
+
}
|
43
|
+
.x-checkbox-undef .x-form-tscheckbox {
|
44
|
+
background-position:0 -26px;
|
45
|
+
}
|
46
|
+
.x-item-disabled .x-form-tscheckbox {
|
47
|
+
background-position:-39px 0;
|
48
|
+
}
|
49
|
+
.x-form-check-over .x-form-tscheckbox {
|
50
|
+
background-position: -13px 0;
|
51
|
+
}
|
52
|
+
.x-form-check-down .x-form-tscheckbox {
|
53
|
+
background-position:-26px 0;
|
54
|
+
}
|
55
|
+
.x-checkbox-checked .x-form-check-over .x-form-tscheckbox {
|
56
|
+
background-position:-13px -13px;
|
57
|
+
}
|
58
|
+
.x-checkbox-checked .x-form-check-down .x-form-tscheckbox {
|
59
|
+
background-position:-26px -13px;
|
60
|
+
}
|
61
|
+
.x-checkbox-checked.x-item-disabled .x-form-tscheckbox {
|
62
|
+
background-position:-39px -13px;
|
63
|
+
}
|
64
|
+
.x-checkbox-undef .x-form-check-over .x-form-tscheckbox {
|
65
|
+
background-position:-13px -26px;
|
66
|
+
}
|
67
|
+
.x-checkbox-undef .x-form-check-down .x-form-tscheckbox {
|
68
|
+
background-position:-26px -26px;
|
69
|
+
}
|
70
|
+
.x-checkbox-undef.x-item-disabled .x-form-tscheckbox {
|
71
|
+
background-position:-39px -26px;
|
31
72
|
}
|