netzke-basepack 0.4.2 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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