netzke-basepack 0.4.2 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.autotest +1 -0
  2. data/.gitignore +6 -0
  3. data/{CHANGELOG → CHANGELOG.rdoc} +26 -0
  4. data/README.rdoc +11 -11
  5. data/Rakefile +37 -11
  6. data/TODO.rdoc +8 -0
  7. data/VERSION +1 -0
  8. data/javascripts/basepack.js +71 -28
  9. data/lib/app/models/netzke_auto_column.rb +56 -0
  10. data/lib/netzke-basepack.rb +5 -3
  11. data/lib/netzke/accordion_panel.rb +69 -67
  12. data/lib/netzke/active_record/basepack.rb +104 -0
  13. data/lib/netzke/active_record/data_accessor.rb +33 -0
  14. data/lib/netzke/basic_app.rb +233 -124
  15. data/lib/netzke/border_layout_panel.rb +97 -98
  16. data/lib/netzke/configuration_panel.rb +24 -0
  17. data/lib/netzke/data_accessor.rb +71 -0
  18. data/lib/netzke/ext.rb +6 -0
  19. data/lib/netzke/field_model.rb +1 -1
  20. data/lib/netzke/fields_configurator.rb +62 -37
  21. data/lib/netzke/form_panel.rb +161 -51
  22. data/lib/netzke/form_panel_api.rb +74 -0
  23. data/lib/netzke/form_panel_js.rb +129 -0
  24. data/lib/netzke/grid_panel.rb +385 -80
  25. data/lib/netzke/grid_panel_api.rb +352 -0
  26. data/lib/netzke/grid_panel_extras/javascripts/rows-dd.js +280 -0
  27. data/lib/netzke/grid_panel_js.rb +721 -0
  28. data/lib/netzke/masquerade_selector.rb +53 -0
  29. data/lib/netzke/panel.rb +9 -0
  30. data/lib/netzke/plugins/configuration_tool.rb +121 -0
  31. data/lib/netzke/property_editor.rb +95 -7
  32. data/lib/netzke/property_editor_extras/helper_model.rb +55 -34
  33. data/lib/netzke/search_panel.rb +62 -0
  34. data/lib/netzke/tab_panel.rb +97 -37
  35. data/lib/netzke/table_editor.rb +49 -44
  36. data/lib/netzke/tree_panel.rb +15 -16
  37. data/lib/netzke/wrapper.rb +29 -5
  38. data/netzke-basepack.gemspec +151 -19
  39. data/stylesheets/basepack.css +5 -0
  40. data/test/app_root/app/models/book.rb +1 -1
  41. data/test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb +1 -1
  42. data/test/unit/accordion_panel_test.rb +1 -2
  43. data/test/unit/active_record_basepack_test.rb +54 -0
  44. data/test/unit/grid_panel_test.rb +8 -12
  45. data/test/unit/helper_model_test.rb +30 -0
  46. metadata +69 -78
  47. data/Manifest +0 -86
  48. data/TODO +0 -3
  49. data/lib/app/models/netzke_hash_record.rb +0 -180
  50. data/lib/app/models/netzke_layout_item.rb +0 -11
  51. data/lib/netzke/ar_ext.rb +0 -269
  52. data/lib/netzke/configuration_tool.rb +0 -80
  53. data/lib/netzke/container.rb +0 -77
  54. data/lib/netzke/db_fields.rb +0 -44
  55. data/lib/netzke/fields_configurator_old.rb +0 -62
  56. data/lib/netzke/form_panel_extras/interface.rb +0 -56
  57. data/lib/netzke/form_panel_extras/js_builder.rb +0 -134
  58. data/lib/netzke/grid_panel_extras/interface.rb +0 -206
  59. data/lib/netzke/grid_panel_extras/js_builder.rb +0 -352
  60. data/test/unit/ar_ext_test.rb +0 -53
  61. data/test/unit/netzke_hash_record_test.rb +0 -52
  62. data/test/unit/netzke_layout_item_test.rb +0 -28
@@ -1,97 +1,103 @@
1
1
  module Netzke
2
+ # Represents the Ext.Panel with layout 'border'. May serve as parent class for compound widgets.
3
+ #
4
+ # == Features:
5
+ # * Responds to region resizing, storing the sizes in persistent config
6
+ #
7
+ # == Future features:
8
+ # * Stores expand/collapse state in the persistent config
9
+ #
10
+ # == Non-functional features:
11
+ # * (JavaScript) Creates convinient methods to access aggregatees inside the regions, like
12
+ # <tt>getCenterWidget()</tt>, <tt>getWestWidget()</tt>, etc
13
+ #
14
+ # == Configuration:
15
+ # <tt>:regions</tt> - a hash in form:
16
+ #
17
+ # {:center => {<netzke widget config>}, :east => {<another netzke widget config>}, ...}
18
+ #
19
+ # <tt>:regions => :center/:west/:etc => :region_config</tt> - configuration options for
20
+ # Ext.layout.BorderLayout.SplitRegion.
21
+ #
22
+ # == Example configuration:
23
+ #
24
+ # :regions => {
25
+ # :center => {:widget_class_name => "Panel", :ext_config => {:html => "A panel"}},
26
+ # :west => {
27
+ # :widget_class_name => "GridPanel",
28
+ # :data_class_name => "User",
29
+ # :region_config => {
30
+ # :width => 100,
31
+ # :split => true
32
+ # }
33
+ # }
34
+ # }
2
35
  class BorderLayoutPanel < Base
3
- interface :resize_region
4
-
5
36
  REGIONS = %w(center west east south north).map(&:to_sym)
6
37
 
7
- #
8
- # JS-class generation
9
- #
10
- module ClassMethods
11
- def js_listeners
12
- {
13
- :afterlayout => {:fn => "this.setResizeEvents".l, :scope => this}
14
- }
15
- end
16
-
17
- def js_before_constructor
18
- <<-JS.l
19
- var items = [];
20
- Ext.each(['center', 'west', 'east', 'south', 'north'], function(r){
21
- var configName = r+'Config';
22
- if (config[configName]){
23
- var regionConfig = config.regions[r] || {};
24
- regionConfig.layout = 'fit';
25
- regionConfig.region = r;
26
- regionConfig.items = [new Ext.netzke.cache[config[configName].widgetClassName](config[configName])]
27
- items.push(regionConfig);
28
-
29
- // A function to access a region widget (even if the widget gets reloaded, the function will work).
30
- // E.g.: getEastWidget()
31
- this['get'+r.capitalize()+'Widget'] = function(){
32
- return this.find('region', r)[0].getWidget()
33
- }.createDelegate(this)
34
-
35
-
36
- };
37
- }, this)
38
- JS
39
- end
40
-
41
- def js_default_config
42
- super.merge({
43
- :layout => 'border',
44
- :items => "items".l
45
- })
46
- end
47
-
48
- def js_extend_properties
49
- {
50
- :get_region_widget => <<-JS.l,
51
- function(region){
52
- return this.find('region', region)[0].getWidget()
53
- }
54
- JS
55
- :set_resize_events => <<-JS.l,
38
+ # JavaScript part
39
+ def self.js_extend_properties
40
+ {
41
+ :layout => 'border',
42
+
43
+ :init_component => <<-END_OF_JAVASCRIPT.l,
56
44
  function(){
57
- this.items.each(function(item, index, length){
58
- if (!item.oldSize) item.oldSize = item.getSize();
59
- if (item.region == 'east' || item.region == 'west') item.on('resize', function(panel, w, h){
60
- if (panel.oldSize.width != w) {
61
- Ext.Ajax.request({
62
- url:this.initialConfig.interface.resizeRegion,
63
- params: {region_name: panel.region, new_width:w}
64
- });
65
- panel.oldSize.width = w;
66
- }
67
- return true;
68
- }, this);
69
- else if (item.region == 'south' || item.region == 'north') item.on('resize', function(panel, w, h){
70
- if (panel.oldSize.height != h) {
71
- Ext.Ajax.request({
72
- url:this.initialConfig.interface.resizeRegion,
73
- params: {region_name: panel.region, new_height:h}
74
- });
75
- panel.oldSize.height = h;
76
- }
77
- return true;
78
- }, this);
45
+ this.items = [];
46
+ Ext.each(['center', 'west', 'east', 'south', 'north'], function(r){
47
+ var configName = r+'Config';
48
+ if (this[configName]){
49
+ var regionConfig = this.regions[r] || {};
50
+ regionConfig.layout = 'fit';
51
+ regionConfig.region = r;
52
+ regionConfig.items = [new Ext.netzke.cache[this[configName].widgetClassName](this[configName])]
53
+ this.items.push(regionConfig);
54
+
55
+ // A function to access a region widget (even if the widget gets reloaded, the function will work).
56
+ // E.g.: getEastWidget()
57
+ this['get'+r.capitalize()+'Widget'] = function(){
58
+ return this.find('region', r)[0].getWidget()
59
+ }.createDelegate(this);
60
+ };
79
61
  }, this);
80
- this.un('afterlayout', this.setResizeEvents, this); // to avoid redefinition of resize events
62
+
63
+ // Now let Ext.Panel do the rest
64
+ Ext.netzke.cache.BorderLayoutPanel.superclass.initComponent.call(this);
65
+
66
+ // First time on "afterlayout", set resize events
67
+ this.on('afterlayout', this.setResizeEvents, this, {single: true});
81
68
  }
82
- JS
83
- }
84
- end
85
- end
86
- extend ClassMethods
87
-
88
- # default instance-level configuration
89
- def initial_config
90
- {
91
- :persistent_config => true
92
- }
69
+ END_OF_JAVASCRIPT
70
+
71
+ :get_region_widget => <<-END_OF_JAVASCRIPT.l,
72
+ function(region){
73
+ return this.find('region', region)[0].getWidget();
74
+ }
75
+ END_OF_JAVASCRIPT
76
+
77
+ :set_resize_events => <<-END_OF_JAVASCRIPT.l,
78
+ function(){
79
+ this.items.each(function(item, index, length){
80
+ if (!item.oldSize) item.oldSize = item.getSize();
81
+ if (item.region == 'east' || item.region == 'west') item.on('resize', function(panel, w, h){
82
+ if (panel.oldSize.width != w) {
83
+ this.resizeRegion({region_name: panel.region, new_width:w});
84
+ panel.oldSize.width = w;
85
+ }
86
+ return true;
87
+ }, this);
88
+ else if (item.region == 'south' || item.region == 'north') item.on('resize', function(panel, w, h){
89
+ if (panel.oldSize.height != h) {
90
+ this.resizeRegion({region_name: panel.region, new_height:h});
91
+ panel.oldSize.height = h;
92
+ }
93
+ return true;
94
+ }, this);
95
+ }, this);
96
+ }
97
+ END_OF_JAVASCRIPT
98
+ }
93
99
  end
94
-
100
+
95
101
  def initial_aggregatees
96
102
  config[:regions] || {}
97
103
  end
@@ -105,25 +111,18 @@ module Netzke
105
111
  REGIONS.each do |r|
106
112
  if region_aggr = aggregatees[r]
107
113
  regions.merge!(r => region_aggr[:region_config] || {})
108
- height = persistent_config["#{r}_height"] ||= regions[r][:height] if regions[r][:height]
109
- width = persistent_config["#{r}_width"] ||= regions[r][:width] if regions[r][:width]
110
- regions[r].merge!(:height => height)
111
- regions[r].merge!(:width => width)
112
114
  end
113
115
  end
114
116
  super.merge(:regions => regions)
115
117
  end
116
-
118
+
119
+ # API
120
+ api :resize_region # handles regions resize
117
121
  def resize_region(params)
118
- persistent_config["#{params["region_name"]}_width"] = params["new_width"].to_i if params["new_width"]
119
- persistent_config["#{params["region_name"]}_height"] = params["new_height"].to_i if params["new_height"]
122
+ persistent_config["regions__#{params["region_name"]}__region_config__width"] = params["new_width"].to_i if params["new_width"]
123
+ persistent_config["regions__#{params["region_name"]}__region_config__height"] = params["new_height"].to_i if params["new_height"]
120
124
  {}
121
125
  end
122
126
 
123
- protected
124
-
125
- def extend_functions; ""; end
126
- def js_extra_events; ""; end
127
-
128
127
  end
129
128
  end
@@ -0,0 +1,24 @@
1
+ module Netzke
2
+ # TabPanel-based widget that wraps-up "configuration widgets" that each widget can define
3
+ # (along) with including the Plugins::ConfigurationTool tool.
4
+ class ConfigurationPanel < TabPanel
5
+ api :commit
6
+ def commit(params)
7
+ commit_data = ActiveSupport::JSON.decode params[:commit_data]
8
+ commit_data.each_pair do |k,v|
9
+ aggregatee_instance(k).commit(v)
10
+ end
11
+ {:reload_parent => true, :feedback => (@flash.empty? ? nil : @flash)}
12
+ end
13
+
14
+ def self.js_extend_properties
15
+ {
16
+ :reload_parent => <<-END_OF_JAVASCRIPT.l,
17
+ function(){
18
+ this.getParent().reload();
19
+ }
20
+ END_OF_JAVASCRIPT
21
+ }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,71 @@
1
+ module Netzke
2
+ # This module is included into such data-driven widgets as GridPanel, FormPanel, etc. It provides for
3
+ # flexible pre-configuration of (virtual) attributes.
4
+ #
5
+ # TODO: show examples of how to create the helpers
6
+ module DataAccessor
7
+
8
+ # This method should be called from the constructor of the widget. It dynamically includes:
9
+ # 1) helpers into the data model for this widget; those may contain instance methods used as virtual attributes
10
+ # 2) generic (used by all "data accessor" widgets) extensions into the data model for this widget
11
+ def apply_helpers
12
+ # Generic extensions to the data model
13
+ if data_class # because some widgets, like FormPanel, may have it optional
14
+ data_class.send(:include, Netzke::ActiveRecord::DataAccessor) if !data_class.include?(Netzke::ActiveRecord::DataAccessor)
15
+
16
+ # Model helpers
17
+ const_name = "Netzke::Helpers::#{data_class.name}"
18
+ model_extensions = const_name.constantize rescue nil
19
+ data_class.send(:include, model_extensions) if model_extensions && !data_class.include?(model_extensions)
20
+ end
21
+ end
22
+
23
+ # Returns columns that are exposed by the class and the helpers
24
+ def predefined_columns
25
+ helper_module = "Netzke::Helpers::#{short_widget_class_name}#{data_class.name}".constantize rescue nil
26
+
27
+ data_class_columns = data_class && data_class.column_names.map(&:to_sym) || []
28
+
29
+ if helper_module
30
+ exposed_attributes = helper_module.respond_to?(:exposed_attributes) ? normalize_array_of_columns(helper_module.exposed_attributes) : nil
31
+ virtual_attributes = helper_module.respond_to?(:virtual_attributes) ? helper_module.virtual_attributes : []
32
+ excluded_attributes = helper_module.respond_to?(:excluded_attributes) ? helper_module.excluded_attributes : []
33
+ attributes_config = helper_module.respond_to?(:attributes_config) ? helper_module.attributes_config : {}
34
+
35
+ res = exposed_attributes || data_class_columns + virtual_attributes
36
+
37
+ res = normalize_columns(res)
38
+
39
+ res.reject!{ |c| excluded_attributes.include? c[:name] }
40
+
41
+ res.map!{ |c| c.merge!(attributes_config[c[:name]] || {})}
42
+ else
43
+ res = normalize_columns(data_class_columns)
44
+ end
45
+
46
+ res
47
+ end
48
+
49
+ # Make sure we have keys as symbols, not strings
50
+ def normalize_array_of_columns(arry)
51
+ arry.map do |f|
52
+ if f.is_a?(Hash)
53
+ f.symbolize_keys
54
+ else
55
+ f.to_sym
56
+ end
57
+ end
58
+ end
59
+
60
+ # From symbol to config hash
61
+ def normalize_column(field)
62
+ field.is_a?(Symbol) ? {:name => field} : field
63
+ end
64
+
65
+ # From symbols to config hashes
66
+ def normalize_columns(items)
67
+ items.map{|c| normalize_column(c)}
68
+ end
69
+
70
+ end
71
+ end
data/lib/netzke/ext.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Netzke
2
+ # Ext-related module. Some constants provide meta-information about the Ext.library.
3
+ module Ext
4
+ FORM_FIELD_XTYPES = %w{ textfield numberfield textarea combobox checkbox xdatetime }
5
+ end
6
+ end
@@ -1,6 +1,6 @@
1
1
  module Netzke
2
2
  class FieldModel < Hash
3
- include ActiveRecordExtensions
3
+ include BasepackActiveRecord
4
4
 
5
5
  def self.new_from_hash(hsh)
6
6
  self.new.replace(hsh)
@@ -1,23 +1,27 @@
1
1
  module Netzke
2
+ # == FieldsConfigurator
3
+ # Provides dynamic configuring columns/fields for GridPanel and FormPanel.
4
+ # Configuration parameters:
5
+ # * <tt>:widget</tt> - widget to configure columns/fields for
2
6
  class FieldsConfigurator < GridPanel
3
- interface :load_defaults
7
+ api :load_defaults
4
8
 
5
- def self.js_base_class
6
- GridPanel
7
- end
8
-
9
9
  def initialize(*args)
10
10
  super
11
-
12
- NetzkeLayoutItem.widget = config[:widget].id_name
13
- config[:data_class_name] = "NetzkeLayoutItem"
11
+ NetzkeAutoColumn.widget = config[:widget]
14
12
  end
15
13
 
16
- def initial_config
17
- super.recursive_merge({
14
+ def default_config
15
+ super.deep_merge({
18
16
  :name => 'columns',
19
- :widget_class_name => "GridPanel",
20
- :ext_config => {:title => false}
17
+ :data_class_name => "NetzkeAutoColumn",
18
+ :ext_config => {
19
+ :header => false,
20
+ :enable_extended_search => false,
21
+ :enable_edit_in_form => false,
22
+ :enable_rows_reordering => GridPanel.config[:rows_reordering_available],
23
+ :enable_pagination => false
24
+ },
21
25
  })
22
26
  end
23
27
 
@@ -27,44 +31,65 @@ module Netzke
27
31
  )
28
32
  end
29
33
 
30
- def bbar
31
- super << "-" << "defaults"
34
+ def independent_config
35
+ res = super
36
+ res[:ext_config][:bbar] = %w{ edit apply - defaults }
37
+ res
32
38
  end
33
-
39
+
40
+ def predefined_columns
41
+ [{:name => :id}, *config[:widget].class.config_columns]
42
+ end
43
+
34
44
  def self.js_extend_properties
35
- super.merge({
36
- :defaults => <<-JS.l,
45
+ {
46
+ # Disable the 'gear' tool for now
47
+ :gear => <<-END_OF_JAVASCRIPT.l,
48
+ function(){
49
+ this.feedback("You can't configure configurator (yet)");
50
+ }
51
+ END_OF_JAVASCRIPT
52
+
53
+ # we need to provide this function so that the server-side commit would happen
54
+ :get_commit_data => <<-END_OF_JAVASCRIPT.l,
55
+ function(){
56
+ return null; // because our commit data is already at the server
57
+ }
58
+ END_OF_JAVASCRIPT
59
+
60
+ :defaults => <<-END_OF_JAVASCRIPT.l,
37
61
  function(){
38
62
  Ext.Msg.confirm('Confirm', 'Are you sure?', function(btn){
39
63
  if (btn == 'yes') {
40
- Ext.Ajax.request({
41
- url:this.initialConfig.interface.loadDefaults,
42
- callback:function(){
43
- this.store.reload();
44
- },
45
- scope:this
46
- })
64
+ this.loadDefaults();
47
65
  }
48
66
  }, this);
49
67
  }
50
- JS
51
- })
68
+ END_OF_JAVASCRIPT
69
+ }
52
70
  end
53
71
 
54
72
  def load_defaults(params)
55
- persistent_config.for_widget(config[:widget].id_name){ |p| p[:layout__columns] = config[:widget].default_db_fields }
56
- # NetzkeLayoutItem.data = config[:widget].default_db_fields
73
+ config[:widget].persistent_config[:layout__columns] = config[:widget].default_columns
74
+ NetzkeAutoColumn.rebuild_table
75
+ {:load_store_data => get_data, :reconfigure => js_config}
76
+ end
77
+
78
+ def commit(params)
79
+ defaults_hash = config[:widget].class.config_columns.inject({}){ |r, c| r.merge!(c[:name] => c[:default]) }
80
+ config[:widget].persistent_config[:layout__columns] = NetzkeAutoColumn.all_columns.map do |c|
81
+ # reject all keys that are 1) same as defaults, 2) 'position'
82
+ c.reject!{ |k,v| defaults_hash[k.to_sym].to_s == v.to_s || k == 'position'}
83
+ c = c["name"] if c.keys.count == 1 # denormalize the column
84
+ c
85
+ end
57
86
  {}
58
87
  end
59
-
60
- def default_db_fields_with_widget_change
61
- NetzkeLayoutItem.widget = config[:widget].id_name
62
- res = default_db_fields_without_widget_change
63
- NetzkeLayoutItem.widget = id_name
64
- res
88
+
89
+ # each time that we are loaded into the app, rebuild the table
90
+ def before_load
91
+ NetzkeAutoColumn.rebuild_table
65
92
  end
66
-
67
- alias_method_chain :default_db_fields, :widget_change
68
-
93
+
69
94
  end
70
95
  end