netzke-basepack 0.7.7 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. data/.travis.yml +15 -10
  2. data/{CHANGELOG.rdoc → CHANGELOG.md} +146 -110
  3. data/LICENSE +7 -1
  4. data/README.md +47 -56
  5. data/Rakefile +5 -5
  6. data/config/before-travis.sh +10 -0
  7. data/javascripts/basepack.js +0 -130
  8. data/javascripts/netzkeremotecombo.js +59 -0
  9. data/lib/netzke/basepack.rb +9 -14
  10. data/lib/netzke/basepack/accordion.rb +45 -0
  11. data/lib/netzke/basepack/active_record.rb +12 -0
  12. data/lib/netzke/basepack/active_record/relation_extensions.rb +27 -0
  13. data/lib/netzke/basepack/columns.rb +309 -0
  14. data/lib/netzke/basepack/data_accessor.rb +22 -12
  15. data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +75 -11
  16. data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +154 -49
  17. data/lib/netzke/basepack/fields.rb +162 -0
  18. data/lib/netzke/basepack/form.rb +136 -0
  19. data/lib/netzke/basepack/{form_panel → form}/javascripts/comma_list_cbg.js +0 -1
  20. data/lib/netzke/basepack/{form_panel/javascripts/form_panel.js → form/javascripts/form.js} +20 -26
  21. data/lib/netzke/basepack/{form_panel → form}/javascripts/n_radio_group.js +0 -1
  22. data/lib/netzke/basepack/{form_panel → form}/javascripts/readonly_mode.js +0 -0
  23. data/lib/netzke/basepack/form/services.rb +115 -0
  24. data/lib/netzke/basepack/{form_panel → form}/stylesheets/readonly_mode.css +0 -0
  25. data/lib/netzke/basepack/grid.rb +355 -0
  26. data/lib/netzke/basepack/{grid_panel → grid}/javascripts/advanced_search.js +1 -1
  27. data/lib/netzke/basepack/{grid_panel → grid}/javascripts/check_column_fix.js +0 -0
  28. data/lib/netzke/basepack/{grid_panel → grid}/javascripts/edit_in_form.js +3 -3
  29. data/lib/netzke/basepack/{grid_panel → grid}/javascripts/event_handling.js +5 -2
  30. data/lib/netzke/basepack/{grid_panel/javascripts/grid_panel.js → grid/javascripts/grid.js} +120 -132
  31. data/lib/netzke/basepack/{grid_panel → grid}/javascripts/misc.js +0 -0
  32. data/lib/netzke/basepack/grid/services.rb +216 -0
  33. data/lib/netzke/basepack/item_persistence.rb +44 -0
  34. data/lib/netzke/basepack/item_persistence/events_plugin.rb +47 -0
  35. data/lib/netzke/basepack/{paging_form_panel.rb → paging_form.rb} +24 -30
  36. data/lib/netzke/basepack/{paging_form_panel/javascripts/paging_form_panel.js → paging_form/javascripts/paging_form.js} +2 -4
  37. data/lib/netzke/basepack/query_builder.rb +44 -73
  38. data/lib/netzke/basepack/query_builder/javascripts/query_builder.js +16 -2
  39. data/lib/netzke/basepack/record_form_window.rb +67 -0
  40. data/lib/netzke/basepack/search_panel.rb +22 -24
  41. data/lib/netzke/basepack/search_panel/javascripts/condition_field.js +2 -2
  42. data/lib/netzke/basepack/search_window.rb +47 -53
  43. data/lib/netzke/basepack/simple_app.rb +10 -13
  44. data/lib/netzke/basepack/simple_app/javascripts/simple_app.js +2 -8
  45. data/lib/netzke/basepack/tab_panel.rb +5 -4
  46. data/lib/netzke/basepack/tab_panel/javascripts/tab_panel.js +5 -5
  47. data/lib/netzke/basepack/version.rb +2 -2
  48. data/lib/netzke/basepack/viewport.rb +16 -0
  49. data/lib/netzke/basepack/window.rb +27 -18
  50. data/lib/netzke/basepack/window/javascripts/window.js +7 -1
  51. data/lib/netzke/basepack/wrap_lazy_loaded.rb +18 -18
  52. data/locales/en.yml +40 -24
  53. data/netzke-basepack.gemspec +51 -82
  54. data/stylesheets/basepack.css +0 -41
  55. data/test/basepack_test_app/Gemfile +9 -46
  56. data/test/basepack_test_app/Gemfile.lock +61 -96
  57. data/test/basepack_test_app/app/components/author_form.rb +8 -5
  58. data/test/basepack_test_app/app/components/author_grid.rb +2 -2
  59. data/test/basepack_test_app/app/components/book_form.rb +34 -31
  60. data/test/basepack_test_app/app/components/book_form_with_defaults.rb +6 -7
  61. data/test/basepack_test_app/app/components/book_form_with_file_upload.rb +10 -0
  62. data/test/basepack_test_app/app/components/book_form_with_nested_attributes.rb +5 -6
  63. data/test/basepack_test_app/app/components/book_grid.rb +19 -8
  64. data/test/basepack_test_app/app/components/book_grid_filtering.rb +4 -7
  65. data/test/basepack_test_app/app/components/book_grid_loader.rb +28 -15
  66. data/test/basepack_test_app/app/components/book_grid_with_custom_columns.rb +45 -21
  67. data/test/basepack_test_app/app/components/book_grid_with_default_values.rb +26 -8
  68. data/test/basepack_test_app/app/components/book_grid_with_excluded_columns.rb +11 -0
  69. data/test/basepack_test_app/app/components/book_grid_with_extra_feedback.rb +2 -2
  70. data/test/basepack_test_app/app/components/book_grid_with_extra_filters.rb +7 -6
  71. data/test/basepack_test_app/app/components/book_grid_with_mass_assignment_security.rb +9 -0
  72. data/test/basepack_test_app/app/components/book_grid_with_nested_attributes.rb +9 -9
  73. data/test/basepack_test_app/app/components/book_grid_with_overridden_columns.rb +5 -3
  74. data/test/basepack_test_app/app/components/book_grid_with_paging.rb +6 -8
  75. data/test/basepack_test_app/app/components/book_grid_with_persistence.rb +6 -4
  76. data/test/basepack_test_app/app/components/book_grid_with_scope.rb +6 -0
  77. data/test/basepack_test_app/app/components/book_grid_with_scoped_authors.rb +10 -7
  78. data/test/basepack_test_app/app/components/book_grid_with_virtual_attributes.rb +21 -13
  79. data/test/basepack_test_app/app/components/book_paging_form.rb +21 -0
  80. data/test/basepack_test_app/app/components/book_query_builder.rb +7 -6
  81. data/test/basepack_test_app/app/components/book_with_custom_primary_key_grid.rb +6 -7
  82. data/test/basepack_test_app/app/components/books_bound_to_author.rb +9 -7
  83. data/test/basepack_test_app/app/components/border_layout_panel_with_persistence.rb +12 -0
  84. data/test/basepack_test_app/app/components/double_book_grid.rb +19 -14
  85. data/test/basepack_test_app/app/components/form_without_model.rb +15 -16
  86. data/test/basepack_test_app/app/components/grid_with_initial_sorting.rb +7 -0
  87. data/test/basepack_test_app/app/components/grid_with_inline_data.rb +7 -0
  88. data/test/basepack_test_app/app/components/paging_form_with_search.rb +2 -2
  89. data/test/basepack_test_app/app/components/panel_with_persistent_regions.rb +35 -0
  90. data/test/basepack_test_app/app/components/query_builder.rb +7 -0
  91. data/test/basepack_test_app/app/components/simple_panel.rb +16 -11
  92. data/test/basepack_test_app/app/components/simple_window.rb +7 -6
  93. data/test/basepack_test_app/app/components/some_accordion.rb +18 -0
  94. data/test/basepack_test_app/app/components/some_auth_app.rb +5 -5
  95. data/test/basepack_test_app/app/components/some_border_layout.rb +20 -20
  96. data/test/basepack_test_app/app/components/some_search_panel.rb +6 -0
  97. data/test/basepack_test_app/app/components/some_simple_app.rb +30 -16
  98. data/test/basepack_test_app/app/components/some_tab_panel.rb +18 -15
  99. data/test/basepack_test_app/app/components/user_form.rb +18 -16
  100. data/test/basepack_test_app/app/components/user_form_with_default_fields.rb +5 -6
  101. data/test/basepack_test_app/app/components/user_grid.rb +11 -6
  102. data/test/basepack_test_app/app/components/user_grid_with_customized_form_fields.rb +5 -3
  103. data/test/basepack_test_app/app/components/window_component_loader.rb +25 -21
  104. data/test/basepack_test_app/app/models/address.rb +0 -26
  105. data/test/basepack_test_app/app/models/author.rb +0 -31
  106. data/test/basepack_test_app/app/models/book.rb +1 -42
  107. data/test/basepack_test_app/app/models/book_with_custom_primary_key.rb +1 -23
  108. data/test/basepack_test_app/app/models/role.rb +0 -21
  109. data/test/basepack_test_app/app/models/user.rb +0 -24
  110. data/test/basepack_test_app/app/views/layouts/components.html.erb +1 -1
  111. data/test/basepack_test_app/config/application.rb +1 -1
  112. data/test/basepack_test_app/config/database.yml.travis +2 -6
  113. data/test/basepack_test_app/config/initializers/netzke.rb +1 -6
  114. data/test/basepack_test_app/db/schema.rb +14 -14
  115. data/test/basepack_test_app/features/accordion_panel.feature +2 -2
  116. data/test/basepack_test_app/features/form_panel.feature +7 -7
  117. data/test/basepack_test_app/features/grid_panel.feature +93 -39
  118. data/test/basepack_test_app/features/grid_panel_with_custom_primary_key.feature +2 -1
  119. data/test/basepack_test_app/features/grid_sorting.feature +30 -6
  120. data/test/basepack_test_app/features/paging_form_panel.feature +7 -7
  121. data/test/basepack_test_app/features/persistent_regions.feature +30 -0
  122. data/test/basepack_test_app/features/search_in_grid.feature +5 -5
  123. data/test/basepack_test_app/features/simple_app.feature +6 -7
  124. data/test/basepack_test_app/features/step_definitions/form_panel_steps.rb +1 -1
  125. data/test/basepack_test_app/features/step_definitions/generic_steps.rb +109 -4
  126. data/test/basepack_test_app/features/step_definitions/grid_panel_steps.rb +8 -10
  127. data/test/basepack_test_app/features/step_definitions/window_steps.rb +27 -0
  128. data/test/basepack_test_app/features/tab_panel.feature +1 -1
  129. data/test/basepack_test_app/features/window.feature +17 -0
  130. data/test/unit/accordion_panel_test.rb +2 -2
  131. data/test/unit/grid_panel_test.rb +4 -4
  132. metadata +57 -83
  133. data/TODO.rdoc +0 -8
  134. data/lib/generators/netzke/basepack_generator.rb +0 -10
  135. data/lib/generators/netzke/templates/assets/ts-checkbox.gif +0 -0
  136. data/lib/generators/netzke/templates/create_netzke_field_lists.rb +0 -18
  137. data/lib/netzke/active_record.rb +0 -20
  138. data/lib/netzke/active_record/attributes.rb +0 -259
  139. data/lib/netzke/active_record/combobox_options.rb +0 -16
  140. data/lib/netzke/active_record/relation_extensions.rb +0 -37
  141. data/lib/netzke/basepack/accordion_panel.rb +0 -39
  142. data/lib/netzke/basepack/action_column.rb +0 -68
  143. data/lib/netzke/basepack/action_column/javascripts/action_column.js +0 -61
  144. data/lib/netzke/basepack/auth_app.rb +0 -159
  145. data/lib/netzke/basepack/basic_app.rb +0 -7
  146. data/lib/netzke/basepack/border_layout_panel.rb +0 -53
  147. data/lib/netzke/basepack/border_layout_panel/javascripts/border_layout_panel.js +0 -40
  148. data/lib/netzke/basepack/data_adapters/data_mapper_adapter.rb +0 -264
  149. data/lib/netzke/basepack/data_adapters/sequel_adapter.rb +0 -260
  150. data/lib/netzke/basepack/form_panel.rb +0 -144
  151. data/lib/netzke/basepack/form_panel/fields.rb +0 -208
  152. data/lib/netzke/basepack/form_panel/javascripts/misc.js +0 -4
  153. data/lib/netzke/basepack/form_panel/services.rb +0 -142
  154. data/lib/netzke/basepack/grid_panel.rb +0 -441
  155. data/lib/netzke/basepack/grid_panel/columns.rb +0 -400
  156. data/lib/netzke/basepack/grid_panel/javascripts/rows-dd.js +0 -281
  157. data/lib/netzke/basepack/grid_panel/record_form_window.rb +0 -41
  158. data/lib/netzke/basepack/grid_panel/services.rb +0 -235
  159. data/lib/netzke/basepack/panel.rb +0 -11
  160. data/lib/netzke/basepack/wrapper.rb +0 -28
  161. data/lib/netzke/data_mapper.rb +0 -18
  162. data/lib/netzke/data_mapper/attributes.rb +0 -273
  163. data/lib/netzke/data_mapper/combobox_options.rb +0 -11
  164. data/lib/netzke/data_mapper/relation_extensions.rb +0 -38
  165. data/lib/netzke/sequel.rb +0 -18
  166. data/lib/netzke/sequel/attributes.rb +0 -274
  167. data/lib/netzke/sequel/combobox_options.rb +0 -10
  168. data/lib/netzke/sequel/relation_extensions.rb +0 -40
  169. data/locales/zh-cn.yml +0 -79
  170. data/test/basepack_test_app/app/components/book_form_with_custom_fields.rb +0 -21
  171. data/test/basepack_test_app/app/components/book_grid_with_column_actions.rb +0 -15
  172. data/test/basepack_test_app/app/components/book_grid_with_defaults.rb +0 -6
  173. data/test/basepack_test_app/app/components/book_paging_form_panel.rb +0 -22
  174. data/test/basepack_test_app/app/components/generic_user_form.rb +0 -12
  175. data/test/basepack_test_app/app/components/simple_accordion.rb +0 -11
  176. data/test/basepack_test_app/app/components/simple_tab_panel.rb +0 -11
  177. data/test/basepack_test_app/app/components/simple_wrapper.rb +0 -7
  178. data/test/basepack_test_app/app/components/some_accordion_panel.rb +0 -22
  179. data/test/basepack_test_app/app/presenters/forms/generic_user.rb +0 -6
  180. data/test/basepack_test_app/app/views/components/loadable_window.html.erb +0 -9
  181. data/test/basepack_test_app/app/views/components/simple_panel.html.erb +0 -1
  182. data/test/basepack_test_app/features/components_in_view.feature +0 -11
  183. data/test/basepack_test_app/features/simple_panel.feature +0 -11
  184. data/test/basepack_test_app/features/validations_in_grid.feature +0 -13
  185. data/test/basepack_test_app/features/virtual_attributes.feature +0 -16
  186. data/test/basepack_test_app/spec/components/form_panel_spec.rb +0 -53
  187. data/test/basepack_test_app/spec/components/grid_panel_spec.rb +0 -10
  188. data/test/basepack_test_app/spec/data_adapter/adapter_spec.rb +0 -68
  189. data/test/basepack_test_app/spec/data_adapter/attributes_spec.rb +0 -56
  190. data/test/basepack_test_app/spec/data_adapter/relation_extensions_spec.rb +0 -125
  191. data/test/basepack_test_app/spec/factories.rb +0 -28
  192. data/test/basepack_test_app/spec/spec_helper.rb +0 -39
@@ -48,4 +48,3 @@ Ext.define('Ext.netzke.form.CommaListCbg', {
48
48
  }
49
49
 
50
50
  });
51
-
@@ -2,7 +2,6 @@
2
2
  bodyStyle : 'padding:5px 5px 0',
3
3
  autoScroll : true,
4
4
  fieldDefaults : { labelWidth : 150 },
5
- applyMask : { msg: "Updating..." },
6
5
 
7
6
  defaults : {
8
7
  anchor : '-20', // to leave some space for the scrollbar
@@ -39,7 +38,7 @@
39
38
 
40
39
  this.initialConfig.errorReader = new ErrorReader();
41
40
 
42
- // Now let Ext.form.FormPanel do the rest
41
+ // Now let Ext.form.Form do the rest
43
42
  this.callParent(arguments);
44
43
 
45
44
  // To inform the parent about the apply event
@@ -131,48 +130,44 @@
131
130
  delete values[fieldName];
132
131
  }
133
132
  }
134
- // WIP: commented out on 2011-05-11 because of fatal errors
135
- // apply mask
136
- // if (!this.applyMaskCmp) this.applyMaskCmp = new Ext.LoadMask(this.bwrap, this.applyMask);
137
- // this.applyMaskCmp.show();
133
+
134
+ // loading mask
135
+ this.setLoading(true);
138
136
 
139
137
  // We must use a different approach when the form is multipart, as we can't use the endpoint
140
- if (this.fileUpload) {
138
+ if (this.getForm().hasUpload()) {
141
139
  this.getForm().submit({ // normal submit
142
140
  url: this.endpointUrl("netzke_submit"),
143
141
  params: {
144
142
  data: Ext.encode(values) // here are the correct values that may be different from display values
145
143
  },
146
144
  failure: function(form, action){
147
- if (this.applyMaskCmp) this.applyMaskCmp.hide();
145
+ var respObj = Ext.decode(action.response.responseText);
146
+ delete respObj.success;
147
+ this.netzkeBulkExecute(respObj);
148
+ this.setLoading(false);
148
149
  },
149
150
  success: function(form, action) {
150
- try {
151
- var respObj = Ext.decode(action.response.responseText);
152
- delete respObj.success;
153
- this.bulkExecute(respObj);
154
- this.fireEvent('submitsuccess');
155
- }
156
- catch(e) {
157
- Ext.Msg.alert('File upload error', action.response.responseText);
158
- }
159
- if (this.applyMaskCmp) this.applyMaskCmp.hide();
151
+ var respObj = Ext.decode(action.response.responseText);
152
+ delete respObj.success;
153
+ this.netzkeBulkExecute(respObj);
160
154
  },
161
155
  scope: this
162
156
  });
163
157
  } else {
164
- this.netzkeSubmit(Ext.apply((this.baseParams || {}), {data:Ext.encode(values)}), function(success){
165
- if (success) {
166
- this.fireEvent("submitsuccess");
167
- if (this.mode == "lockable") this.setReadonlyMode(true);
168
- };
169
- if (this.applyMaskCmp) this.applyMaskCmp.hide();
170
- }, this);
158
+ this.netzkeSubmit(Ext.apply((this.baseParams || {}), { data:Ext.encode(values) }), function(){ this.setLoading(false); }, this);
171
159
  }
172
160
  }
173
161
  this.fireEvent('afterApply', this);
174
162
  },
175
163
 
164
+ // called from the server
165
+ onSubmitSuccess: function() {
166
+ this.fireEvent("submitsuccess");
167
+ if (this.mode == "lockable") this.setReadonlyMode(true);
168
+ this.setLoading(false);
169
+ },
170
+
176
171
  setFormValues: function(values){
177
172
  var assocValues = values.meta.associationValues || {};
178
173
  for (var assocFieldName in assocValues) {
@@ -220,5 +215,4 @@
220
215
  }
221
216
  }, this);
222
217
  }
223
-
224
218
  }
@@ -40,4 +40,3 @@ Ext.define('Ext.netzke.form.NRadioGroup', {
40
40
  });
41
41
  }
42
42
  });
43
-
@@ -0,0 +1,115 @@
1
+ module Netzke
2
+ module Basepack
3
+ class Form < Netzke::Base
4
+ module Services
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ #
9
+ # Endpoints
10
+
11
+ # Called when the form gets submitted (e.g. by pressing the Apply button)
12
+ endpoint :netzke_submit do |params, this|
13
+ data = ActiveSupport::JSON.decode(params[:data])
14
+ data.each_pair do |k,v|
15
+ data[k]=nil if v.blank? || v == "null" # Ext JS returns "null" on empty date fields, or "" for not filled optional integer fields, which gives errors when passed to model (at least in DataMapper)
16
+ end
17
+
18
+ # File uploads are in raw params instead of "data" hash, so, mix them in into "data"
19
+ controller.params.each_pair do |k,v|
20
+ data[k] = v if v.is_a?(ActionDispatch::Http::UploadedFile)
21
+ end
22
+
23
+ success = create_or_update_record(data)
24
+
25
+ if success
26
+ this.set_form_values(js_record_data)
27
+ this.success = true # respond to classic form submission with {success: true}
28
+ this.on_submit_success # inform the Netzke endpoint caller about success
29
+ else
30
+ # flash eventual errors
31
+ data_adapter.errors_array(@record).each do |error|
32
+ flash :error => error
33
+ end
34
+ this.netzke_feedback(@flash)
35
+ this.apply_form_errors(build_form_errors(record))
36
+ end
37
+ end
38
+
39
+ # Can be called when the form needs to load a record with given ID. E.g.:
40
+ #
41
+ # someForm.netzkeLoad({id: 100});
42
+ endpoint :netzke_load do |params, this|
43
+ @record = data_class && data_adapter.find_record(params[:id])
44
+ this.set_form_values js_record_data
45
+ end
46
+
47
+ # Returns options for a combobox
48
+ # params receive:
49
+ # +attr+ - column's name
50
+ # +query+ - what's typed-in in the combobox
51
+ # +id+ - selected record id
52
+ endpoint :get_combobox_options do |params, this|
53
+ attr = fields[params[:attr].to_sym]
54
+ this.data = data_adapter.combo_data(attr, params[:query])
55
+ end
56
+
57
+ end
58
+
59
+ def values
60
+ record && record.netzke_hash(fields)
61
+ end
62
+
63
+ private
64
+
65
+ # Builds the form errors
66
+ def build_form_errors(record)
67
+ form_errors = {}
68
+ foreign_keys = data_adapter.hash_fk_model
69
+ record.errors.to_hash.map{|field, error|
70
+ # some ORM return an array for error
71
+ error = error.join ', ' if error.kind_of? Array
72
+ # Get the correct field name for the errors on foreign keys
73
+ if foreign_keys.has_key?(field)
74
+ fields.each do |k, v|
75
+ # Hack to stop to_nifty_json from camalizing model__field
76
+ field = k.to_s.gsub('__', '____') if k.to_s.split('__').first == foreign_keys[field].to_s
77
+ end
78
+ end
79
+ form_errors[field] ||= []
80
+ form_errors[field] << error
81
+ }
82
+ form_errors
83
+ end
84
+
85
+ # Creates/updates a record from hash
86
+ def create_or_update_record(hsh)
87
+ hsh.merge!(config[:strong_default_attrs]) if config[:strong_default_attrs]
88
+ @record ||= data_adapter.find_record hsh.delete(data_class.primary_key.to_s) # only pick up the record specified in the params if it was not provided in the configuration
89
+ #data_class.find(:first, :conditions => {data_class.primary_key => hsh.delete(data_class.primary_key)})
90
+ success = true
91
+
92
+ @record = data_class.new if @record.nil?
93
+
94
+ hsh.each_pair do |k,v|
95
+ data_adapter.set_record_value_for_attribute(@record, fields[k.to_sym].nil? ? {:name => k} : fields[k.to_sym], v, config.role || :default)
96
+ end
97
+
98
+ #hsh.each_pair do |k,v|
99
+ #begin
100
+ #@record.send("#{k}=",v)
101
+ #rescue StandardError => exc
102
+ #flash :error => exc.message
103
+ #success = false
104
+ #break
105
+ #end
106
+ #end
107
+
108
+ # did we have complete success?
109
+ success && data_adapter.save_record(@record)
110
+ end
111
+
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,355 @@
1
+ require "netzke/basepack/grid/services"
2
+
3
+ module Netzke
4
+ module Basepack
5
+ # {Ext.grid.Panel}[http://docs.sencha.com/ext-js/4-1/#!/api/Ext.grid.Panel] -based component with the following features:
6
+ #
7
+ # * automatic column configuration based on used ORM
8
+ # * pagination
9
+ # * multi-line CRUD operations
10
+ # * (multe-record) editing and adding records through a form
11
+ # * one-to-many association support
12
+ # * server-side sorting
13
+ # * filtering
14
+ # * complex query search with preset management
15
+ # * persistent column resizing, moving and hiding
16
+ # * permissions
17
+ # * virtual attribute support
18
+ #
19
+ # == Instance configuration
20
+ # The following config options are supported:
21
+ # [:+model+]
22
+ # Name of the ActiveRecord model that provides data to this Grid, e.g. "User"
23
+ # [:+columns+]
24
+ # An array of columns to be displayed in the grid; each column may be represented by a symbol (representing the model's attribute name), or a hash (when extra configuration is needed). See the "Columns" section below.
25
+ # [:+scope+]
26
+ # Specifies how the data should be filtered.
27
+ # When it's a symbol, it's used as a scope name.
28
+ # When it's a string, it's a SQL statement (passed directly to +where+).
29
+ # When it's a hash, it's a conditions hash (passed directly to +where+).
30
+ # When it's an array, it's expanded into an SQL statement with arguments (passed directly to +where+), e.g.:
31
+ #
32
+ # scope: ["id > ?", 100])
33
+ #
34
+ # [:+role+]
35
+ # Role for ActiveModel mass-assignment security
36
+ # [:+strong_default_attrs+]
37
+ # (defaults to {}) a hash of attributes to be merged atop of every created/updated record, e.g. +role_id: 1+
38
+ # [:+enable_column_filters+]
39
+ # (defaults to true) enable filters in column's context menu
40
+ # [:+enable_edit_in_form+]
41
+ # (defaults to true) provide buttons into the toolbar that activate editing/adding records via a form
42
+ # [:+enable_extended_search+]
43
+ # (defaults to true) provide a button into the toolbar that shows configurable search form
44
+ # [:+enable_context_menu+]
45
+ # (defaults to true) enable rows context menu
46
+ # [:+context_menu+]
47
+ # An array of actions (e.g. [:edit, "-", :del] - see the Actions section) or +false+ to disable the context menu
48
+ # [:+enable_pagination+]
49
+ # (defaults to true) enable pagination
50
+ # [:+rows_per_page+]
51
+ # (defaults to 30) number of rows per page (ignored when +enable_pagination+ is set to +false+)
52
+ # [:+load_inline_data+]
53
+ # (defaults to false) grid is being loaded along with its initial data; use with precaution, preferred method is auto-loading of data in a separate server request (see +data_store+)
54
+ # [:+data_store+]
55
+ # (defaults to {}) extra configuration for the JS class's internal store (see {Ext.data.Store}[http://docs.sencha.com/ext-js/4-1/#!/api/Ext.data.Store] ). For example, to disable auto loading of data, do:
56
+ #
57
+ # data_store: {auto_load: false}
58
+ #
59
+ # == Columns
60
+ # Columns are configured by passing an array to the +columns+ option. Each element in the array is either the name of model's (virtual) attribute (in which case the configuration will be fully automatic), or a hash that may contain the following configuration options as keys:
61
+ #
62
+ # [:+name+]
63
+ # (required) name of the column, that may correspond to the model's (virtual) attribute
64
+ # [:+read_only+]
65
+ # A boolean that defines if the cells in the column should be editable
66
+ # [:+editable+]
67
+ # Same as +read_only+, but in reverse (takes precedence over +read_only+)
68
+ # [:+filterable+]
69
+ # Set to false to disable filtering on this column
70
+ # [:+getter+]
71
+ # A lambda that receives a record as a parameter, and is expected to return a string that will be sent to the cell (can be HTML code), e.g.:
72
+ #
73
+ # getter: ->(r){ [r.first_name, r.last_name].join }
74
+ #
75
+ # [:+setter+]
76
+ # A lambda that receives a record as first parameter, and the value passed from the cell as the second parameter, and is expected to modify the record accordingly, e.g.:
77
+ #
78
+ # :setter => ->(r,v){ r.first_name, r.last_name = v.split(" ") }
79
+ #
80
+ # [:+scope+]
81
+ # The scope for one-to-many association column. Same syntax applies as for scoping out records for the grid itself. See "One-to-many association support" for details.
82
+ #
83
+ # [:+sorting_scope+]
84
+ # The name of the scope used for sorting the column. This can be useful for virtual columns for example. The scope will get one parameter specifying the direction (:asc or :desc). Example:
85
+ #
86
+ # columns => [{ name: "complete_user_name", sorting_scope: :sort_user_by_full_name }, ...]
87
+ #
88
+ # class User < ActiveRecord::Base
89
+ # scope :sort_user_by_full_name, ->(dir){
90
+ # order("users.first_name #{dir.to_s}, users.last_name #{dir.to_s}")
91
+ # }
92
+ # end
93
+ #
94
+ # [:+format+]
95
+ # The format to display data in case of date and datetime columns, e.g. 'Y-m-d g:i:s'.
96
+ # [:+excluded+]
97
+ # When true, this column will not be used in the grid (not even in the hidden mode)
98
+ # [:+blank_line+]
99
+ # The blank line for one-to-many association columns, defaults to "---". Set to false to exclude completely.
100
+ #
101
+ # Besides these options, a column can receive any meaningful config option understood by {Ext.grid.column.Column}(http://docs.sencha.com/ext-js/4-1/#!/api/Ext.grid.column.Column) (e.g. +hidden+)
102
+ #
103
+ # === Customizing columns by extending Grid
104
+ # Grid itself always uses the columns provided in the `columns` config option. But this behavior can be changed by overriding the `columns` method, which follows the same semantics as the `columns` config option. This can be used, for example, for extending the list of columns provided in the config:
105
+ #
106
+ # def columns
107
+ # super + [:extra_column]
108
+ # end
109
+ #
110
+ #
111
+ # == One-to-many association support
112
+ # If the model bound to a grid +belongs_to+ another model, Grid can display an "assocition column" - where the user can select the associated record from a drop-down box. You can specify which method of the association should be used as the display value for the drop-down box options by using the double-underscore notation on the column name, where the association name is separated from the association method by "__" (double underscore). For example, let's say we have a Book that +belongs_to+ model Author, and Author responds to +first_name+. This way, the book grid can have a column defined as follows:
113
+ #
114
+ # {name: "author__first_name"}
115
+ #
116
+ # Grid will detect it to be an association column, and will use the drop-down box for selecting an author, where the list of authors will be represented by the author's first name.
117
+ #
118
+ # In order to scope out the records displayed in the drop-down box, the +scope+ column option can be used, e.g.:
119
+ #
120
+ # {name: "author__first_name", scope: ->(relation){relation.where(:popular => true)}
121
+ #
122
+ # == Add/edit forms
123
+ # The forms will by default display the fields that correspond to the configured columns, taking over meaningful configuration options (e.g. +text+ will be converted into +fieldLabel+).
124
+ # You may override the default fields displayed in the forms by overriding the +default_fields_for_forms+ method, which should return an array understood by the +items+ config property of the +Form+. If you need to use a custom class instead of +Form+, you need to override the +preconfigure_record_window+ method:
125
+ #
126
+ # def preconfigure_record_window(c)
127
+ # super
128
+ # c.form_config.klass = UserForm
129
+ # end
130
+ #
131
+ #
132
+ # == Actions
133
+ # You can override Grid's actions to change their text, icons, and tooltips (see http://rdoc.info/github/netzke/netzke-core/Netzke/Core/Actions).
134
+ #
135
+ # Grid implements the following actions:
136
+ # [:+add+]
137
+ # Inline adding of a record
138
+ # [:+del+]
139
+ # Deletion of records
140
+ # [:+edit+]
141
+ # Inline editing of a record
142
+ # [:+apply+]
143
+ # Applying inline changes
144
+ # [:+add_in_form+]
145
+ # Adding a record in a form
146
+ # [:+edit_in_form+]
147
+ # (multi-record) editing in a forrm
148
+ # [:+search+]
149
+ # Advanced searching
150
+ #
151
+ #
152
+ #
153
+ # == Class-level configuration
154
+ #
155
+ # Configuration on this level is effective during the life-time of the application. One place for setting these options is initializers:
156
+ #
157
+ # Netzke::Basepack::Grid.setup do |c|
158
+ # c.edit_in_form_available = false
159
+ # c.advanced_search_available = false
160
+ # c.column_filters_available = false
161
+ # end
162
+ #
163
+ # Most of these options influence the amount of JavaScript code that is generated for this component's class, in the way that the less functionality is enabled, the less code is generated.
164
+ #
165
+ # The following class configuration options are available:
166
+ # [:+column_filters_available+]
167
+ # (defaults to true) include code for the filters in the column's context menu
168
+ # [:+edit_in_form_available+]
169
+ # (defaults to true) include code for (multi-record) editing and adding records through a form
170
+ # [:+advanced_search_available+]
171
+ # (defaults to true) include code for extended configurable search
172
+ class Grid < Netzke::Base
173
+ include self::Services
174
+ include Columns
175
+ include DataAccessor
176
+ include Netzke::Core::ConfigToDslDelegator
177
+
178
+ class_attribute :column_filters_available
179
+ self.column_filters_available = true
180
+
181
+ class_attribute :advanced_search_available
182
+ self.advanced_search_available = true
183
+
184
+ class_attribute :edit_in_form_available
185
+ self.edit_in_form_available = true
186
+
187
+ # JavaScript class configuration
188
+ js_configure do |c|
189
+ c.extend = "Ext.grid.Panel"
190
+ c.mixin :grid, :event_handling
191
+ c.mixin :advanced_search if advanced_search_available
192
+ c.mixin :edit_in_form if edit_in_form_available
193
+
194
+ c.translate *%w[are_you_sure confirmation]
195
+
196
+ # JavaScript includes
197
+ ex = Netzke::Core.ext_path.join("examples")
198
+
199
+ c.require ex.join("ux/CheckColumn.js")
200
+ c.require :check_column_fix
201
+
202
+ # Includes for column filters
203
+ if column_filters_available
204
+ [
205
+ "ux/grid/menu/ListMenu.js",
206
+ "ux/grid/menu/RangeMenu.js",
207
+ "ux/grid/FiltersFeature.js"
208
+ ].each{ |path| c.require(ex.join(path)) }
209
+
210
+ %w{Boolean Date List Numeric String}.unshift("").each do |f|
211
+ c.require(ex.join"ux/grid/filter/#{f}Filter.js")
212
+ end
213
+ end
214
+ end
215
+
216
+ # Allows children classes to simply do:
217
+ #
218
+ # model "User"
219
+ delegates_to_dsl :model
220
+
221
+ def configure(c)
222
+ # Defaults. The nil? checks are needed because these can be already set in a subclass
223
+ c.enable_edit_in_form = self.class.edit_in_form_available if c.enable_edit_in_form.nil?
224
+ c.enable_extended_search = self.class.advanced_search_available if c.enable_extended_search.nil?
225
+ c.enable_column_filters = self.class.column_filters_available if c.enable_column_filters.nil?
226
+ c.enable_pagination = true if c.enable_pagination.nil?
227
+ c.rows_per_page = 30 if c.rows_per_page.nil?
228
+ c.tools = %w{ refresh } if c.tools.nil?
229
+
230
+ super
231
+ end
232
+
233
+ def js_configure(c) #:nodoc:
234
+ super
235
+
236
+ c.title = c.title || self.class.js_config.properties[:title] || data_class.name.pluralize
237
+ c.bbar = bbar
238
+ c.context_menu = context_menu
239
+ c.columns = final_columns(with_meta: true)
240
+ c.columns_order = columns_order
241
+ c.inline_data = get_data if c.load_inline_data
242
+ c.pri = data_adapter.primary_key
243
+ end
244
+
245
+ def config
246
+ @config ||= ActiveSupport::OrderedOptions.new.tap do |c|
247
+ # extend with data_store convenient config object
248
+ c.data_store = ActiveSupport::OrderedOptions.new
249
+ end
250
+ end
251
+
252
+ def bbar
253
+ config.has_key?(:bbar) ? config[:bbar] : default_bbar
254
+ end
255
+
256
+ def context_menu
257
+ config.has_key?(:context_menu) ? config[:context_menu] : default_context_menu
258
+ end
259
+
260
+ # Override to change the default bottom toolbar
261
+ def default_bbar
262
+ res = %w{ add edit apply del }.map(&:to_sym)
263
+ res << "-" << :add_in_form << :edit_in_form if config[:enable_edit_in_form]
264
+ res << "-" << :search if config[:enable_extended_search]
265
+ res
266
+ end
267
+
268
+ # Override to change the default context menu
269
+ def default_context_menu
270
+ res = %w{ edit del }.map(&:to_sym)
271
+ res << "-" << :edit_in_form if config[:enable_edit_in_form]
272
+ res
273
+ end
274
+
275
+ action :add do |a|
276
+ a.disabled = config[:prohibit_create]
277
+ a.handler = "onAddInline" # overriding naming conventions as Ext 4 grid has its own onAdd method
278
+ a.icon = :add
279
+ end
280
+
281
+ action :edit do |a|
282
+ a.disabled = true
283
+ a.icon = :table_edit
284
+ end
285
+
286
+ action :del do |a|
287
+ a.disabled = true
288
+ a.icon = :table_row_delete
289
+ end
290
+
291
+ action :apply do |a|
292
+ a.disabled = config[:prohibit_update] && config[:prohibit_create]
293
+ a.icon = :tick
294
+ end
295
+
296
+ action :add_in_form do |a|
297
+ a.icon = :application_form_add
298
+ end
299
+
300
+ action :edit_in_form do |a|
301
+ a.disabled = true
302
+ a.icon = :application_form_edit
303
+ end
304
+
305
+ action :search do |a|
306
+ a.enable_toggle = true
307
+ a.icon = :find
308
+ end
309
+
310
+ component :add_window do |c|
311
+ preconfigure_record_window(c)
312
+ c.title = "Add #{data_class.model_name.human}"
313
+ c.items = [:add_form]
314
+ c.form_config.record = data_class.new(columns_default_values)
315
+ end
316
+
317
+ component :edit_window do |c|
318
+ preconfigure_record_window(c)
319
+ c.title = "Edit #{data_class.model_name.human}"
320
+ c.items = [:edit_form]
321
+ end
322
+
323
+ component :multi_edit_window do |c|
324
+ preconfigure_record_window(c)
325
+ c.title = "Edit #{data_class.model_name.human.pluralize}"
326
+ c.items = [:multi_edit_form]
327
+ end
328
+
329
+ component :search_window do |c|
330
+ c.klass = SearchWindow
331
+ c.model = config.model
332
+ c.fields = default_fields_for_forms
333
+ end
334
+
335
+ protected
336
+
337
+ def preconfigure_record_window(c)
338
+ c.klass = RecordFormWindow
339
+
340
+ c.form_config = ActiveSupport::OrderedOptions.new.tap do |f|
341
+ f.model = config[:model]
342
+ f.persistent_config = config[:persistent_config]
343
+ f.strong_default_attrs = config[:strong_default_attrs]
344
+ f.mode = config[:mode]
345
+ f.items = default_fields_for_forms
346
+ end
347
+ end
348
+
349
+ def self.server_side_config_options
350
+ super + [:scope]
351
+ end
352
+
353
+ end
354
+ end
355
+ end