netzke-basepack 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/CHANGELOG +18 -0
  2. data/LICENSE +20 -0
  3. data/Manifest +64 -0
  4. data/README.mdown +74 -0
  5. data/Rakefile +14 -0
  6. data/generators/netzke_basepack/USAGE +8 -0
  7. data/generators/netzke_basepack/netzke_basepack_generator.rb +8 -0
  8. data/generators/netzke_basepack/netzke_grid_generator.rb +7 -0
  9. data/generators/netzke_basepack/templates/create_netzke_grid_columns.rb +21 -0
  10. data/init.rb +1 -0
  11. data/install.rb +1 -0
  12. data/javascripts/basepack.js +52 -0
  13. data/javascripts/filters.js +7 -0
  14. data/lib/app/models/netzke_grid_column.rb +23 -0
  15. data/lib/netzke-basepack.rb +28 -0
  16. data/lib/netzke/accordion.rb +11 -0
  17. data/lib/netzke/ar_ext.rb +163 -0
  18. data/lib/netzke/column.rb +43 -0
  19. data/lib/netzke/container.rb +81 -0
  20. data/lib/netzke/grid.rb +120 -0
  21. data/lib/netzke/grid_interface.rb +156 -0
  22. data/lib/netzke/grid_js_builder.rb +276 -0
  23. data/lib/netzke/preference_grid.rb +43 -0
  24. data/lib/netzke/properties_tool.rb +66 -0
  25. data/lib/netzke/property_grid.rb +60 -0
  26. data/netzke-basepack.gemspec +38 -0
  27. data/tasks/netzke_basepack_tasks.rake +4 -0
  28. data/test/app_root/app/controllers/application.rb +2 -0
  29. data/test/app_root/app/models/book.rb +9 -0
  30. data/test/app_root/app/models/category.rb +2 -0
  31. data/test/app_root/app/models/city.rb +3 -0
  32. data/test/app_root/app/models/continent.rb +2 -0
  33. data/test/app_root/app/models/country.rb +3 -0
  34. data/test/app_root/app/models/genre.rb +3 -0
  35. data/test/app_root/config/boot.rb +114 -0
  36. data/test/app_root/config/database.yml +21 -0
  37. data/test/app_root/config/environment.rb +13 -0
  38. data/test/app_root/config/environments/in_memory.rb +0 -0
  39. data/test/app_root/config/environments/mysql.rb +0 -0
  40. data/test/app_root/config/environments/postgresql.rb +0 -0
  41. data/test/app_root/config/environments/sqlite.rb +0 -0
  42. data/test/app_root/config/environments/sqlite3.rb +0 -0
  43. data/test/app_root/config/routes.rb +4 -0
  44. data/test/app_root/db/migrate/20081222033343_create_books.rb +15 -0
  45. data/test/app_root/db/migrate/20081222033440_create_genres.rb +14 -0
  46. data/test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb +18 -0
  47. data/test/app_root/db/migrate/20081223024935_create_categories.rb +13 -0
  48. data/test/app_root/db/migrate/20081223025635_create_countries.rb +14 -0
  49. data/test/app_root/db/migrate/20081223025653_create_continents.rb +13 -0
  50. data/test/app_root/db/migrate/20081223025732_create_cities.rb +15 -0
  51. data/test/app_root/script/console +7 -0
  52. data/test/ar_ext_test.rb +39 -0
  53. data/test/column_test.rb +27 -0
  54. data/test/console_with_fixtures.rb +4 -0
  55. data/test/fixtures/books.yml +9 -0
  56. data/test/fixtures/categories.yml +7 -0
  57. data/test/fixtures/cities.yml +21 -0
  58. data/test/fixtures/continents.yml +7 -0
  59. data/test/fixtures/countries.yml +9 -0
  60. data/test/fixtures/genres.yml +9 -0
  61. data/test/grid_test.rb +43 -0
  62. data/test/netzke_basepack_test.rb +8 -0
  63. data/test/schema.rb +10 -0
  64. data/test/test_helper.rb +20 -0
  65. data/uninstall.rb +1 -0
  66. metadata +162 -0
@@ -0,0 +1,43 @@
1
+ module Netzke
2
+ class Column
3
+ def self.default_columns_for_widget(widget)
4
+ raise ArgumentError, "No data_class_name specified for widget #{widget.config[:name]}" if widget.config[:data_class_name].nil?
5
+
6
+ # layout = NetzkeLayout.create(:widget_name => widget.id_name, :items_class => self.name, :user_id => NetzkeLayout.user_id)
7
+
8
+ data_class = widget.config[:data_class_name].constantize
9
+
10
+ exposed_columns = normalize_columns(data_class.exposed_columns) # columns exposed from the data class
11
+
12
+ columns_from_config = widget.config[:columns] && normalize_columns(widget.config[:columns]) # columns specified in widget's config
13
+
14
+ if columns_from_config
15
+ # reverse-merge each column hash from config with each column hash from exposed_columns (columns from config have higher priority)
16
+ for c in columns_from_config
17
+ corresponding_exposed_column = exposed_columns.find{ |k| k[:name] == c[:name] }
18
+ c.reverse_merge!(corresponding_exposed_column) if corresponding_exposed_column
19
+ end
20
+ columns_for_create = columns_from_config
21
+ else
22
+ # we didn't have columns configured in widget's config, so, use the columns from the data class
23
+ columns_for_create = exposed_columns
24
+ end
25
+
26
+ res = []
27
+ for c in columns_for_create
28
+ # finally reverse-merge them with the defaults from the data_class
29
+ res << data_class.default_column_config(c)
30
+ end
31
+
32
+ res
33
+ end
34
+
35
+ protected
36
+
37
+ # like this: [:col1, {:name => :col2}, :col3] => [{:name => :col1}, {:name => :col2}, {:name => :col3}]
38
+ def self.normalize_columns(items)
39
+ items.map{|c| c.is_a?(Symbol) ? {:name => c} : c}
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,81 @@
1
+ module Netzke
2
+ #
3
+ # Base class for Accordion and TabPanel widgets, it shouldn't be used as a stand-alone class.
4
+ #
5
+ class Container < Base
6
+ def initialize(*args)
7
+ super
8
+ for item in initial_items do
9
+ add_aggregatee item
10
+ items << item.keys.first
11
+ end
12
+ end
13
+
14
+ def initial_dependencies
15
+ dep = super
16
+ for item in items
17
+ candidate_dependency = aggregatees[item][:widget_class_name]
18
+ dep << candidate_dependency unless dep.include?(candidate_dependency)
19
+ end
20
+ dep
21
+ end
22
+
23
+ def js_before_constructor
24
+ js_widget_items
25
+ end
26
+
27
+ def items
28
+ @items ||= []
29
+ end
30
+
31
+ def initial_items
32
+ config[:items] || []
33
+ end
34
+
35
+ def js_widget_items
36
+ res = ""
37
+ item_aggregatees.each_pair do |k,v|
38
+ next if v[:late_aggregation]
39
+ res << <<-JS
40
+ var #{k.to_js} = new Ext.componentCache['#{v[:widget_class_name]}'](config.#{k.to_js}Config);
41
+ JS
42
+ end
43
+ res
44
+ end
45
+
46
+ def js_items
47
+ items.inject([]) do |a,i|
48
+ a << {
49
+ :title => i.to_s.humanize,
50
+ :layout => 'fit',
51
+ :id => i.to_s,
52
+ # :id => "#{config[:name]}_#{i.to_s}",
53
+ :items => ([i.to_s.to_js.l] if !aggregatees[i][:late_aggregation]),
54
+ # these listeners will be different for tab_panel and accordion
55
+ :collapsed => !aggregatees[i][:active],
56
+ :listeners => {
57
+ # :activate => {:fn => "function(p){this.feedback(p.id)}".l, :scope => this},
58
+ :expand => {:fn => "this.loadItemWidget".l, :scope => this}
59
+ }
60
+ }
61
+ end
62
+ end
63
+
64
+ def js_extend_properties
65
+ {
66
+ # loads widget into the panel if it's not loaded yet
67
+ :load_item_widget => <<-JS.l,
68
+ function(panel) {
69
+ if (!panel.getWidget()) panel.loadWidget(this.id + "__" + panel.id + "__get_widget");
70
+ // if (!this.getWidgetByPanel(panel)) this.loadWidget(panel, this.initialConfig[panel.id+'Config'].interface.getWidget);
71
+ }
72
+ JS
73
+ }
74
+ end
75
+
76
+ protected
77
+ def item_aggregatees
78
+ aggregatees.delete_if{|k,v| !@items.include?(k)}
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,120 @@
1
+ require 'searchlogic'
2
+ module Netzke
3
+ #
4
+ # Functionality:
5
+ # * data operations - get, post, delete, create
6
+ # * column resize and move
7
+ # * permissions
8
+ # * sorting
9
+ # * pagination
10
+ # * properties and column configuration
11
+ #
12
+ class Grid < Base
13
+ include GridJsBuilder
14
+ include GridInterface
15
+
16
+ # define connection points between client side and server side of Grid. See implementation of equally named methods in the GridInterface module.
17
+ interface :get_data, :post_data, :delete_data, :resize_column, :move_column, :get_cb_choices
18
+
19
+ def initial_config
20
+ {
21
+ :ext_config => {:properties => true, :column_filters => true},
22
+ :layout_manager => "NetzkeLayout",
23
+ :column_resize => true,
24
+ :column_move => true
25
+ }
26
+ end
27
+
28
+ def property_widgets
29
+ [{
30
+ :columns => {
31
+ :widget_class_name => "Grid",
32
+ :data_class_name => column_manager_class_name,
33
+ :ext_config => {:title => false, :properties => false},
34
+ :active => true
35
+ }
36
+ },{
37
+ :general => {
38
+ :widget_class_name => "PreferenceGrid",
39
+ :host_widget_name => @id_name,
40
+ :default_properties => available_permissions.map{ |k| {:name => "permissions.#{k}", :value => @permissions[k.to_sym]}},
41
+ :ext_config => {:title => false}
42
+ }
43
+ }]
44
+ end
45
+
46
+ ## Data for properties grid
47
+ def properties__columns__get_data(params = {})
48
+ columns_widget = aggregatee_instance(:properties__columns)
49
+
50
+ layout_id = layout_manager_class.by_widget(id_name).id
51
+ columns_widget.interface_get_data(params.merge(:filters => {:layout_id => layout_id}))
52
+ end
53
+
54
+ def properties__general__load_source(params = {})
55
+ w = aggregatee_instance(:properties__general)
56
+ w.interface_load_source(params)
57
+ end
58
+
59
+
60
+
61
+ protected
62
+
63
+ def layout_manager_class
64
+ config[:layout_manager] && config[:layout_manager].constantize
65
+ end
66
+
67
+ def column_manager_class_name
68
+ "NetzkeGridColumn"
69
+ end
70
+
71
+ def column_manager_class
72
+ column_manager_class_name.constantize
73
+ rescue NameError
74
+ nil
75
+ end
76
+
77
+ def available_permissions
78
+ %w(read update create delete)
79
+ end
80
+
81
+ public
82
+
83
+ # get columns from layout manager
84
+ def get_columns
85
+ if layout_manager_class
86
+ layout = layout_manager_class.by_widget(id_name)
87
+ layout ||= column_manager_class.create_layout_for_widget(self)
88
+ layout.items_hash # TODO: bad name!
89
+ else
90
+ Netzke::Column.default_columns_for_widget(self)
91
+ end
92
+ end
93
+
94
+ def tools
95
+ [{:id => 'refresh', :on => {:click => 'refreshClick'}}]
96
+ end
97
+
98
+ def actions
99
+ [{
100
+ :text => 'Add', :handler => 'add', :disabled => @pref['permissions.create'] == false
101
+ },{
102
+ :text => 'Edit', :handler => 'edit', :disabled => @pref['permissions.update'] == false
103
+ },{
104
+ :text => 'Delete', :handler => 'delete', :disabled => @pref['permissions.delete'] == false
105
+ },{
106
+ :text => 'Apply', :handler => 'submit', :disabled => @pref['permissions.update'] == false && @pref['permissions.create'] == false
107
+ }]
108
+ end
109
+
110
+
111
+
112
+ # Uncomment to enable a menu duplicating the actions
113
+ # def js_menus
114
+ # [{:text => "config.dataClassName".l, :menu => "config.actions".l}]
115
+ # end
116
+
117
+ # include ColumnOperations
118
+ include PropertiesTool # it will load aggregation with name :properties into a modal window
119
+ end
120
+ end
@@ -0,0 +1,156 @@
1
+ module Netzke::GridInterface
2
+ def post_data(params)
3
+ [:create, :update].each do |operation|
4
+ data = JSON.parse(params.delete("#{operation}d_records".to_sym)) if params["#{operation}d_records".to_sym]
5
+ process_data(data, operation) if !data.nil?
6
+ end
7
+ {:success => true, :flash => @flash}
8
+ end
9
+
10
+ def get_data(params = {})
11
+ if @permissions[:read]
12
+ records = get_records(params)
13
+ {:data => records, :total => records.total_records}
14
+ else
15
+ flash :error => "You don't have permissions to read data"
16
+ {:success => false, :flash => @flash}
17
+ end
18
+ end
19
+
20
+ def delete_data(params = {})
21
+ if @permissions[:delete]
22
+ record_ids = JSON.parse(params.delete(:records))
23
+ klass = config[:data_class_name].constantize
24
+ klass.delete(record_ids)
25
+ flash :notice => "Deleted #{record_ids.size} record(s)"
26
+ success = true
27
+ else
28
+ flash :error => "You don't have permissions to delete data"
29
+ success = false
30
+ end
31
+ {:success => success, :flash => @flash}
32
+ end
33
+
34
+ def resize_column(params)
35
+ raise "Called interface_resize_column while not configured to do so" unless config[:column_resize]
36
+ l_item = layout_manager_class.by_widget(id_name).layout_items[params[:index].to_i]
37
+ l_item.width = params[:size]
38
+ l_item.save!
39
+ {}
40
+ end
41
+
42
+ def move_column(params)
43
+ raise "Called interface_move_column while not configured to do so" unless config[:column_move]
44
+ layout_manager_class.by_widget(id_name).move_item(params[:old_index].to_i, params[:new_index].to_i)
45
+ {}
46
+ end
47
+
48
+ # Return the choices for the column
49
+ def get_cb_choices(params)
50
+ column = params[:column]
51
+ query = params[:query]
52
+
53
+ {:data => config[:data_class_name].constantize.choices_for(column, query).map{|s| [s]}}
54
+ end
55
+
56
+
57
+ protected
58
+
59
+ # operation => :update || :create
60
+ def process_data(data, operation)
61
+ if @permissions[operation]
62
+ klass = config[:data_class_name].constantize
63
+ modified_records = 0
64
+ data.each do |record_hash|
65
+ record = operation == :create ? klass.create : klass.find(record_hash.delete('id'))
66
+ logger.debug { "!!! record: #{record.inspect}" }
67
+ success = true
68
+ exception = nil
69
+
70
+ # process all attirubutes for the same record (OPTIMIZE: we can use update_attributes separately for regular attributes to speed things up)
71
+ record_hash.each_pair do |k,v|
72
+ begin
73
+ record.send("#{k}=",v)
74
+ rescue ArgumentError => exc
75
+ flash :error => exc.message
76
+ success = false
77
+ break
78
+ end
79
+ end
80
+
81
+ # try to save
82
+ modified_records += 1 if success && record.save
83
+
84
+ # flash eventual errors
85
+ record.errors.each_full do |msg|
86
+ flash :error => msg
87
+ end
88
+
89
+ flash :notice => "#{operation.to_s.capitalize}d #{modified_records} records"
90
+ end
91
+ else
92
+ flash :error => "You don't have permissions to #{operation} data"
93
+ end
94
+ end
95
+
96
+ # get records
97
+ def get_records(params)
98
+ search_params = normalize_params(params)
99
+ raise ArgumentError, "No data_class_name specified for widget '#{config[:name]}'" if !config[:data_class_name]
100
+ records = config[:data_class_name].constantize.all(search_params.clone) # clone needed as searchlogic removes :conditions key from the hash
101
+ output_array = []
102
+ records.each do |r|
103
+ r_array = []
104
+ self.get_columns.each do |column|
105
+ r_array << r.send(column[:name])
106
+ end
107
+ output_array << r_array
108
+ end
109
+
110
+ # add total_entries accessor to the result
111
+ class << output_array
112
+ attr :total_records, true
113
+ end
114
+ total_records_count = config[:data_class_name].constantize.count(search_params)
115
+ output_array.total_records = total_records_count
116
+
117
+ output_array
118
+ end
119
+
120
+ def convert_filters(column_filter)
121
+ res = {}
122
+ column_filter.each_pair do |k,v|
123
+ field = v["field"]
124
+ case v["data"]["type"]
125
+ when "string"
126
+ field << "_contains"
127
+ when "numeric"
128
+ field << "_#{v["data"]["comparison"]}"
129
+ end
130
+ value = v["data"]["value"]
131
+ res.merge!({field => value})
132
+ end
133
+ res
134
+ end
135
+
136
+ # make params understandable to searchlogic
137
+ def normalize_params(params)
138
+ # filters
139
+ conditions = params[:filter] && convert_filters(params[:filter])
140
+
141
+ normalized_conditions = {}
142
+ conditions && conditions.each_pair do |k, v|
143
+ assoc, method = k.split('__')
144
+ normalized_conditions.merge!(method.nil? ? {assoc => v} : {assoc => {method => v}})
145
+ end
146
+
147
+ # sorting
148
+ order_by = if params[:sort]
149
+ assoc, method = params[:sort].split('__')
150
+ method.nil? ? assoc : {assoc => method}
151
+ end
152
+
153
+ page = params[:start].to_i/params[:limit].to_i + 1 if params[:limit]
154
+ {:per_page => params[:limit], :page => page, :order_by => order_by, :order_as => params[:dir], :conditions => normalized_conditions}
155
+ end
156
+ end
@@ -0,0 +1,276 @@
1
+ module Netzke::GridJsBuilder
2
+ def js_base_class
3
+ 'Ext.grid.EditorGridPanel'
4
+ end
5
+
6
+ def js_bbar
7
+ <<-JS.l
8
+ (config.rowsPerPage) ? new Ext.PagingToolbar({
9
+ pageSize:config.rowsPerPage,
10
+ items:config.actions,
11
+ store:ds,
12
+ emptyMsg:'Empty'}) : config.actions
13
+ JS
14
+ end
15
+
16
+ def js_default_config
17
+ super.merge({
18
+ :store => "ds".l,
19
+ :cm => "cm".l,
20
+ :sel_model => "new Ext.grid.RowSelectionModel()".l,
21
+ :auto_scroll => true,
22
+ :click_to_edit => 2,
23
+ :track_mouse_over => true,
24
+ # :bbar => "config.actions".l,
25
+ :bbar => js_bbar,
26
+ :plugins => "plugins".l,
27
+
28
+ #custom configs
29
+ :auto_load_data => true
30
+ })
31
+ end
32
+
33
+ def js_before_constructor
34
+ <<-JS
35
+ var plugins = [];
36
+ if (!config.columns) this.feedback('No columns defined for grid '+config.id);
37
+ this.recordConfig = [];
38
+ Ext.each(config.columns, function(column){this.recordConfig.push({name:column.name})}, this);
39
+ this.Row = Ext.data.Record.create(this.recordConfig);
40
+
41
+ var ds = new Ext.data.Store({
42
+ proxy: this.proxy = new Ext.data.HttpProxy({url:config.interface.getData}),
43
+ reader: new Ext.data.ArrayReader({root: "data", totalProperty: "total", successProperty: "succes", id:0}, this.Row),
44
+ remoteSort: true,
45
+ listeners:{'loadexception':{
46
+ fn:this.loadExceptionHandler,
47
+ scope:this
48
+ }}
49
+ });
50
+
51
+ this.cmConfig = [];
52
+ Ext.each(config.columns, function(c){
53
+ var editor = c.readOnly ? null : Ext.netzke.editors[c.showsAs](c, config);
54
+
55
+ this.cmConfig.push({
56
+ header: c.label || c.name,
57
+ dataIndex: c.name,
58
+ hidden: c.hidden,
59
+ width: c.width,
60
+ editor: editor,
61
+ sortable: true
62
+ })
63
+ }, this);
64
+
65
+ var cm = new Ext.grid.ColumnModel(this.cmConfig);
66
+
67
+ this.addEvents("refresh");
68
+
69
+ // Filters
70
+ if (config.columnFilters) {
71
+ var filters = []
72
+ Ext.each(config.columns, function(c){
73
+ filters.push({type:Ext.netzke.filterMap[c.showsAs], dataIndex:c.name})
74
+ })
75
+ var gridFilters = new Ext.grid.GridFilters({filters:filters});
76
+ plugins.push(gridFilters);
77
+ }
78
+
79
+ JS
80
+ end
81
+
82
+ def js_config
83
+ res = super
84
+ # we pass column config at the time of instantiating the JS class
85
+ res.merge!(:columns => get_columns || config[:columns]) # first try to get columns from DB, then from config
86
+ res.merge!(:data_class_name => config[:data_class_name])
87
+ res
88
+ end
89
+
90
+ def js_listeners
91
+ super.merge({
92
+ :columnresize => (config[:column_resize] ? {:fn => "this.onColumnResize".l, :scope => this} : nil),
93
+ :columnmove => (config[:column_move] ? {:fn => "this.onColumnMove".l, :scope => this} : nil)
94
+ })
95
+ end
96
+
97
+
98
+ def js_extend_properties
99
+ {
100
+ :on_widget_load => <<-JS.l,
101
+ function(){
102
+ // auto-load
103
+ if (this.initialConfig.autoLoadData) {
104
+ // if we have a paging toolbar, load the first page, otherwise
105
+ if (this.getBottomToolbar().changePage) this.getBottomToolbar().changePage(0); else this.store.load();
106
+ }
107
+ }
108
+ JS
109
+
110
+ :load_exception_handler => <<-JS.l,
111
+ function(proxy, options, response, error){
112
+ if (response.status == 200 && (responseObject = Ext.decode(response.responseText)) && responseObject.flash){
113
+ this.feedback(responseObject.flash)
114
+ } else {
115
+ if (error){
116
+ this.feedback(error.message);
117
+ } else {
118
+ this.feedback(response.statusText)
119
+ }
120
+ }
121
+ }
122
+ JS
123
+
124
+ :add => <<-JS.l,
125
+ function(){
126
+ var rowConfig = {};
127
+ Ext.each(this.initialConfig.columns, function(c){
128
+ rowConfig[c.name] = c.defaultValue || ''; // FIXME: if the user is happy with all the defaults, the record won't be 'dirty'
129
+ }, this);
130
+
131
+ var r = new this.Row(rowConfig); // TODO: add default values
132
+ r.set('id', -r.id); // to distinguish new records by negative values
133
+ this.stopEditing();
134
+ this.store.add(r);
135
+ this.store.newRecords = this.store.newRecords || []
136
+ this.store.newRecords.push(r);
137
+ // console.info(this.store.newRecords);
138
+ this.tryStartEditing(this.store.indexOf(r));
139
+ }
140
+ JS
141
+
142
+ :edit => <<-JS.l,
143
+ function(){
144
+ var row = this.getSelectionModel().getSelected();
145
+ if (row){
146
+ this.tryStartEditing(this.store.indexOf(row))
147
+ }
148
+ }
149
+ JS
150
+
151
+ # try editing the first editable (not hidden, not read-only) sell
152
+ :try_start_editing => <<-JS.l,
153
+ function(row){
154
+ if (row == null) return;
155
+ var editableColumns = this.getColumnModel().getColumnsBy(function(columnConfig, index){
156
+ return !columnConfig.hidden && !!columnConfig.editor;
157
+ });
158
+ // console.info(editableColumns);
159
+ var firstEditableColumn = editableColumns[0];
160
+ if (firstEditableColumn){
161
+ this.startEditing(row, firstEditableColumn.id);
162
+ }
163
+ }
164
+ JS
165
+
166
+ :delete => <<-JS.l,
167
+ function() {
168
+ if (this.getSelectionModel().hasSelection()){
169
+ Ext.Msg.confirm('Confirm', 'Are you sure?', function(btn){
170
+ if (btn == 'yes') {
171
+ var records = []
172
+ this.getSelectionModel().each(function(r){
173
+ records.push(r.get('id'));
174
+ }, this);
175
+ Ext.Ajax.request({
176
+ url: this.initialConfig.interface.deleteData,
177
+ params: {records: Ext.encode(records)},
178
+ success:function(r){
179
+ var m = Ext.decode(r.responseText);
180
+ this.store.reload();
181
+ // this.loadWithFeedback();
182
+ this.feedback(m.flash);
183
+ },
184
+ scope:this
185
+ });
186
+ }
187
+ }, this);
188
+ }
189
+ }
190
+ JS
191
+ :submit => <<-JS.l,
192
+ function(){
193
+
194
+ var newRecords = [];
195
+ if (this.store.newRecords){
196
+ Ext.each(this.store.newRecords, function(r){
197
+ newRecords.push(r.getChanges())
198
+ r.commit() // commit the changes, so that they are not picked up by getModifiedRecords() further down
199
+ }, this);
200
+ delete this.store.newRecords;
201
+ }
202
+
203
+ var updatedRecords = [];
204
+ Ext.each(this.store.getModifiedRecords(),
205
+ function(record) {
206
+ var completeRecordData = {};
207
+ Ext.apply(completeRecordData, Ext.apply(record.getChanges(), {id:record.get('id')}));
208
+ updatedRecords.push(completeRecordData);
209
+ },
210
+ this);
211
+
212
+ if (newRecords.length > 0 || updatedRecords.length > 0) {
213
+ Ext.Ajax.request({
214
+ url:this.initialConfig.interface.postData,
215
+ params: {
216
+ updated_records: Ext.encode(updatedRecords),
217
+ created_records: Ext.encode(newRecords),
218
+ filters: this.store.baseParams.filters
219
+ },
220
+ success:function(response){
221
+ var m = Ext.decode(response.responseText);
222
+ if (m.success) {
223
+ this.store.reload();
224
+ // this.loadWithFeedback();
225
+ this.store.commitChanges();
226
+ this.feedback(m.flash);
227
+ } else {
228
+ this.feedback(m.flash);
229
+ }
230
+ },
231
+ failure:function(response){
232
+ this.feedback('Bad response from server');
233
+ },
234
+ scope:this
235
+ });
236
+ }
237
+
238
+ }
239
+ JS
240
+
241
+ :refresh_click => <<-JS.l,
242
+ function() {
243
+ // console.info(this);
244
+ // if (this.fireEvent('refresh', this) !== false) this.loadWithFeedback();
245
+ if (this.fireEvent('refresh', this) !== false) this.store.reload();
246
+ }
247
+ JS
248
+
249
+ :on_column_resize => <<-JS.l,
250
+ function(index, size){
251
+ // var column = this.getColumnModel().getDataIndex(index);
252
+ Ext.Ajax.request({
253
+ url:this.initialConfig.interface.resizeColumn,
254
+ params:{
255
+ index:index,
256
+ size:size
257
+ }
258
+ })
259
+ }
260
+ JS
261
+
262
+ :on_column_move => <<-JS.l
263
+ function(oldIndex, newIndex){
264
+ Ext.Ajax.request({
265
+ url:this.initialConfig.interface.moveColumn,
266
+ params:{
267
+ old_index:oldIndex,
268
+ new_index:newIndex
269
+ }
270
+ })
271
+ }
272
+ JS
273
+
274
+ }
275
+ end
276
+ end