netzke-basepack 0.5.5.1 → 0.5.6

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 (41) hide show
  1. data/CHANGELOG.rdoc +16 -1
  2. data/README.rdoc +31 -56
  3. data/Rakefile +2 -2
  4. data/TODO.rdoc +3 -0
  5. data/javascripts/basepack.js +70 -30
  6. data/lib/app/models/netzke_auto_column.rb +1 -53
  7. data/lib/app/models/netzke_auto_field.rb +4 -0
  8. data/lib/app/models/netzke_auto_table.rb +61 -0
  9. data/lib/netzke-basepack.rb +13 -7
  10. data/lib/netzke/active_record/basepack.rb +28 -0
  11. data/lib/netzke/active_record/data_accessor.rb +2 -0
  12. data/lib/netzke/basic_app.rb +3 -4
  13. data/lib/netzke/{basic_app_extras → basic_app}/statusbar_ext.js +0 -0
  14. data/lib/netzke/data_accessor.rb +2 -0
  15. data/lib/netzke/ext.rb +1 -1
  16. data/lib/netzke/fields_configurator.rb +22 -6
  17. data/lib/netzke/form_panel.rb +20 -11
  18. data/lib/netzke/form_panel/form_panel_api.rb +78 -0
  19. data/lib/netzke/form_panel/form_panel_js.rb +143 -0
  20. data/lib/netzke/{form_panel_extras → form_panel}/javascripts/netzkefileupload.js +0 -0
  21. data/lib/netzke/{form_panel_extras → form_panel}/javascripts/xcheckbox.js +0 -0
  22. data/lib/netzke/grid_panel.rb +33 -24
  23. data/lib/netzke/grid_panel/grid_panel_api.rb +358 -0
  24. data/lib/netzke/grid_panel/grid_panel_js.rb +747 -0
  25. data/lib/netzke/{grid_panel_extras → grid_panel}/javascripts/filters.js +0 -0
  26. data/lib/netzke/{grid_panel_extras → grid_panel}/javascripts/rows-dd.js +0 -0
  27. data/lib/netzke/grid_panel/record_form_window.rb +44 -0
  28. data/lib/netzke/panel.rb +1 -3
  29. data/lib/netzke/property_editor.rb +2 -0
  30. data/lib/netzke/{property_editor_extras → property_editor}/helper_model.rb +2 -2
  31. data/lib/netzke/search_panel.rb +3 -3
  32. data/lib/netzke/window.rb +9 -3
  33. data/test/unit/grid_panel_test.rb +0 -2
  34. data/test/unit/helper_model_test.rb +2 -2
  35. metadata +16 -15
  36. data/lib/netzke/field_model.rb +0 -131
  37. data/lib/netzke/form_panel_api.rb +0 -78
  38. data/lib/netzke/form_panel_js.rb +0 -141
  39. data/lib/netzke/grid_panel_api.rb +0 -356
  40. data/lib/netzke/grid_panel_extras/record_form_window.rb +0 -46
  41. data/lib/netzke/grid_panel_js.rb +0 -725
@@ -1,3 +1,5 @@
1
+ require "activerecord"
2
+
1
3
  module Netzke::ActiveRecord
2
4
  # Provides extensions to all ActiveRecord-based classes
3
5
  module Basepack
@@ -59,6 +61,32 @@ module Netzke::ActiveRecord
59
61
  super
60
62
  end
61
63
  end
64
+
65
+ # Make respond_to? return true for association assignment method, like "genre__name="
66
+ def respond_to?(method, include_private = false)
67
+ split = method.to_s.split(/__/)
68
+ if split.size > 1
69
+ if split.last =~ /=$/
70
+ if split.size == 2
71
+ # search for association and assign it to self
72
+ assoc = self.class.reflect_on_association(split.first.to_sym)
73
+ assoc_method = split.last.chop
74
+ if assoc
75
+ assoc.klass.respond_to?("find_by_#{assoc_method}")
76
+ else
77
+ super
78
+ end
79
+ else
80
+ super
81
+ end
82
+ else
83
+ # self.respond_to?(split.first) ? self.send(split.first).respond_to?(split[1..-1].join("__")) : false
84
+ super
85
+ end
86
+ else
87
+ super
88
+ end
89
+ end
62
90
 
63
91
  module ClassMethods
64
92
 
@@ -1,3 +1,5 @@
1
+ require 'netzke/active_record/basepack'
2
+
1
3
  module Netzke::ActiveRecord
2
4
  # Provides extensions to those ActiveRecord-based models that provide data to the "data accessor" widgets,
3
5
  # like GridPanel, FormPanel, etc
@@ -14,8 +14,8 @@ module Netzke
14
14
  def self.include_js
15
15
  res = []
16
16
  ext_examples = Netzke::Base.config[:ext_location] + "/examples/"
17
- res << ext_examples + "ux/StatusBar.js"
18
- res << "#{File.dirname(__FILE__)}/basic_app_extras/statusbar_ext.js"
17
+ res << ext_examples + "ux/statusbar/StatusBar.js"
18
+ res << "#{File.dirname(__FILE__)}/basic_app/statusbar_ext.js"
19
19
  end
20
20
 
21
21
  def self.js_base_class
@@ -57,7 +57,6 @@ module Netzke
57
57
  :xtype => 'toolbar',
58
58
  :region => 'north',
59
59
  :height => 25
60
- # :items => ["-"]
61
60
  },{
62
61
  :id => 'main-statusbar',
63
62
  :xtype => 'statusbar',
@@ -115,10 +114,10 @@ module Netzke
115
114
  // var position = toolbar.items.getCount() - 2;
116
115
  // position = position < 0 ? 0 : position;
117
116
  // toolbar.insertButton(position, newMenu);
118
-
119
117
  toolbar.add(item);
120
118
  // this.menus[owner.id].push(newMenu); // TODO: remember the menus from this owner in some other way
121
119
  }, this);
120
+ toolbar.doLayout(); // required since Ext 3.0.3
122
121
  }
123
122
  END_OF_JAVASCRIPT
124
123
 
@@ -1,3 +1,5 @@
1
+ require "netzke/active_record/data_accessor"
2
+
1
3
  module Netzke
2
4
  # This module is included into such data-driven widgets as GridPanel, FormPanel, etc. It provides for
3
5
  # flexible pre-configuration of (virtual) attributes.
data/lib/netzke/ext.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Netzke
2
2
  # Ext-related module. Some constants provide meta-information about the Ext.library.
3
3
  module Ext
4
- FORM_FIELD_XTYPES = %w{ textfield numberfield textarea combobox checkbox xdatetime }
4
+ FORM_FIELD_XTYPES = %w{ textfield numberfield textarea combobox xcheckbox checkbox xdatetime }
5
5
  COLUMN_XTYPES = %w{ gridcolumn booleancolumn numbercolumn datecolumn }
6
6
  end
7
7
  end
@@ -8,13 +8,24 @@ module Netzke
8
8
 
9
9
  def initialize(*args)
10
10
  super
11
- NetzkeAutoColumn.widget = config[:widget]
11
+ @auto_table_klass = is_for_grid? ? NetzkeAutoColumn : NetzkeAutoField
12
+ @auto_table_klass.widget = client_widget
13
+ end
14
+
15
+ # widget that uses us
16
+ def client_widget
17
+ @passed_config[:widget]
18
+ end
19
+
20
+ # is our client widget a grid (as opposed to a form)?
21
+ def is_for_grid?
22
+ client_widget.class.ancestors.include?(GridPanel)
12
23
  end
13
24
 
14
25
  def default_config
15
26
  super.deep_merge({
16
27
  :name => 'columns',
17
- :data_class_name => "NetzkeAutoColumn",
28
+ :data_class_name => is_for_grid? ? "NetzkeAutoColumn" : "NetzkeAutoField",
18
29
  :ext_config => {
19
30
  :header => false,
20
31
  :enable_extended_search => false,
@@ -32,7 +43,7 @@ module Netzke
32
43
  end
33
44
 
34
45
  def default_bbar
35
- %w{ edit apply - defaults }
46
+ %w{ add edit apply del - defaults }
36
47
  end
37
48
 
38
49
  def predefined_columns
@@ -69,13 +80,13 @@ module Netzke
69
80
 
70
81
  def load_defaults(params)
71
82
  config[:widget].persistent_config[:layout__columns] = config[:widget].default_columns
72
- NetzkeAutoColumn.rebuild_table
83
+ @auto_table_klass.rebuild_table
73
84
  {:load_store_data => get_data}
74
85
  end
75
86
 
76
87
  def commit(params)
77
88
  defaults_hash = config[:widget].class.config_columns.inject({}){ |r, c| r.merge!(c[:name] => c[:default]) }
78
- config[:widget].persistent_config[:layout__columns] = NetzkeAutoColumn.all_columns.map do |c|
89
+ config[:widget].persistent_config[:layout__columns] = @auto_table_klass.all_columns.map do |c|
79
90
  # reject all keys that are 1) same as defaults, 2) 'position'
80
91
  c.reject!{ |k,v| defaults_hash[k.to_sym].to_s == v.to_s || k == 'position'}
81
92
  c = c["name"] if c.keys.count == 1 # denormalize the column
@@ -86,7 +97,12 @@ module Netzke
86
97
 
87
98
  # each time that we are loaded into the app, rebuild the table
88
99
  def before_load
89
- NetzkeAutoColumn.rebuild_table
100
+ @auto_table_klass.rebuild_table
101
+ end
102
+
103
+ # Don't show the config tool
104
+ def config_tool_needed?
105
+ false
90
106
  end
91
107
 
92
108
  end
@@ -1,10 +1,15 @@
1
+ require "netzke/form_panel/form_panel_js"
2
+ require "netzke/form_panel/form_panel_api"
3
+ require "netzke/plugins/configuration_tool"
4
+ require "netzke/data_accessor"
5
+
1
6
  module Netzke
2
7
  # = FormPanel
3
8
  #
4
9
  # Represents Ext.form.FormPanel
5
10
  #
6
11
  # == Configuration
7
- # * <tt>:data_class_name</tt> - name of the ActiveRecord model that provides data to this GridPanel.
12
+ # * <tt>:model</tt> - name of the ActiveRecord model that provides data to this GridPanel.
8
13
  # * <tt>:record</tt> - record to be displayd in the form. Takes precedence over <tt>:record_id</tt>
9
14
  # * <tt>:record_id</tt> - id of the record to be displayd in the form. Also see <tt>:record</tt>
10
15
  #
@@ -12,8 +17,8 @@ module Netzke
12
17
  #
13
18
  # * <tt>:mode</tt> - when set to <tt>:config</tt>, FormPanel loads in configuration mode
14
19
  class FormPanel < Base
15
- include Netzke::FormPanelJs # javascript (client-side)
16
- include Netzke::FormPanelApi # API (server-side)
20
+ include FormPanelJs # javascript (client-side)
21
+ include FormPanelApi # API (server-side)
17
22
  include Netzke::DataAccessor # some code shared between GridPanel, FormPanel, and other widgets that use database attributes
18
23
 
19
24
  # Class-level configuration with defaults
@@ -43,9 +48,9 @@ module Netzke
43
48
  # Extra javascripts
44
49
  def self.include_js
45
50
  [
46
- "#{File.dirname(__FILE__)}/form_panel_extras/javascripts/xcheckbox.js",
47
- Netzke::Base.config[:ext_location] + "/examples/ux/FileUploadField.js",
48
- "#{File.dirname(__FILE__)}/form_panel_extras/javascripts/netzkefileupload.js"
51
+ "#{File.dirname(__FILE__)}/form_panel/javascripts/xcheckbox.js",
52
+ Netzke::Base.config[:ext_location] + "/examples/ux/fileuploadfield/FileUploadField.js",
53
+ "#{File.dirname(__FILE__)}/form_panel/javascripts/netzkefileupload.js"
49
54
  ]
50
55
  end
51
56
 
@@ -56,11 +61,14 @@ module Netzke
56
61
  def initialize(*args)
57
62
  super
58
63
  apply_helpers
59
- @record = config[:record] || data_class && data_class.find_by_id(config[:record_id])
64
+ @record = config[:record] || config[:record_id] && data_class && data_class.find(:first, :conditions => {data_class.primary_key => config[:record_id]})
60
65
  end
61
66
 
67
+ # (We can't memoize this method because at some point we extend it, e.g. in Netzke::DataAccessor)
62
68
  def data_class
63
- @data_class ||= config[:data_class_name] && config[:data_class_name].constantize
69
+ ::ActiveSupport::Deprecation.warn("data_class_name option is deprecated. Use model instead", caller) if config[:data_class_name]
70
+ model_name = config[:model] || config[:data_class_name]
71
+ @data_class ||= model_name && model_name.constantize
64
72
  end
65
73
 
66
74
  def configuration_widgets
@@ -98,7 +106,8 @@ module Netzke
98
106
  def js_config
99
107
  res = super
100
108
  res.merge!(:clmns => columns)
101
- res.merge!(:data_class_name => config[:data_class_name])
109
+ res.merge!(:data_class_name => data_class.name) if data_class
110
+ res.merge!(:pri => data_class.primary_key) if data_class
102
111
  res
103
112
  end
104
113
 
@@ -108,10 +117,10 @@ module Netzke
108
117
  {:name => :name, :type => :string, :editor => :combobox, :width => 200},
109
118
  {:name => :hidden, :type => :boolean, :editor => :checkbox, :width => 40, :header => "Excl"},
110
119
  {:name => :disabled, :type => :boolean, :editor => :checkbox, :width => 40, :header => "Dis"},
111
- {:name => :xtype, :type => :string},
120
+ {:name => :xtype, :type => :string, :editor => {:xtype => :combobox, :options => Netzke::Ext::FORM_FIELD_XTYPES}},
112
121
  {:name => :value, :type => :string},
113
122
  {:name => :field_label, :type => :string},
114
- {:name => :input_type, :type => :string}
123
+ {:name => :input_type, :type => :string, :hidden => true}
115
124
  ]
116
125
  end
117
126
 
@@ -0,0 +1,78 @@
1
+ module Netzke
2
+ class FormPanel < Base
3
+ module FormPanelApi
4
+ # API handling form submission
5
+ def netzke_submit(params)
6
+ success = create_or_update_record(params)
7
+
8
+ if success
9
+ {:set_form_values => array_of_values, :set_result => "ok"}
10
+ else
11
+ # flash eventual errors
12
+ @record.errors.each_full do |msg|
13
+ flash :error => msg
14
+ end
15
+ {:feedback => @flash}
16
+ end
17
+ end
18
+
19
+ # Creates/updates a record from hash
20
+ def create_or_update_record(params)
21
+ hsh = ActiveSupport::JSON.decode(params[:data])
22
+ hsh.merge!(config[:strong_default_attrs]) if config[:strong_default_attrs]
23
+ @record ||= data_class.find(:first, :conditions => {data_class.primary_key => hsh.delete(data_class.primary_key)}) # only pick up the record specified in the params if it was not provided in the configuration
24
+ success = true
25
+
26
+ @record = data_class.new if @record.nil?
27
+
28
+ hsh.each_pair do |k,v|
29
+ begin
30
+ @record.send("#{k}=",v)
31
+ rescue StandardError => exc
32
+ logger.debug "!!! FormPanelApi#create_or_update_record exception: #{exc.inspect}\n"
33
+ flash :error => exc.message
34
+ success = false
35
+ break
36
+ end
37
+ end
38
+
39
+ # did we have complete success?
40
+ success && @record.save
41
+ end
42
+
43
+ # API handling form load
44
+ # def load(params)
45
+ # klass = config[:data_class_name].constantize
46
+ # case params[:neighbour]
47
+ # when "previous" then @record = klass.previous(params[:id])
48
+ # when "next" then @record = klass.next(params[:id])
49
+ # else @record = klass.find(params[:id])
50
+ # end
51
+ # {:data => [array_of_values]}
52
+ # end
53
+
54
+ def netzke_load(params)
55
+ @record = data_class && data_class.find_by_id(params[:id])
56
+ {:set_form_values => array_of_values}
57
+ end
58
+
59
+ # API that returns options for a combobox
60
+ def get_combobox_options(params)
61
+ column = params[:column]
62
+ query = params[:query]
63
+
64
+ {:data => data_class.options_for(column, query).map{|s| [s]}}
65
+ end
66
+
67
+ def configuration_panel__fields__get_combobox_options(params)
68
+ query = params[:query]
69
+ {:data => (predefined_columns.map{ |c| c[:name].to_s }).grep(/^#{query}/).map{ |n| [n] }}.to_nifty_json
70
+ end
71
+
72
+ # Returns array of form values according to the configured columns
73
+ def array_of_values
74
+ @record && @record.to_array(columns, self)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,143 @@
1
+ module Netzke
2
+ class FormPanel < Base
3
+ module FormPanelJs
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def js_base_class
10
+ "Ext.FormPanel"
11
+ end
12
+
13
+ def js_extend_properties
14
+ {
15
+ :body_style => 'padding:5px 5px 0',
16
+ :auto_scroll => true,
17
+ :label_width => 150,
18
+ :default_type => 'textfield',
19
+
20
+ :init_component => <<-END_OF_JAVASCRIPT.l,
21
+ function(){
22
+ var recordFields = []; // Record
23
+ this.items = [];
24
+ var index = 0;
25
+
26
+ // Process columns
27
+ Ext.each(this.clmns, function(field){
28
+ if (typeof field == 'string') field = {name:field}; // normalize field
29
+ if (!field.hidden || field.name == this.pri) {
30
+ recordFields.push({name:field.name, mapping:index++});
31
+
32
+ var defaultColumnConfig = Ext.apply({}, this.defaultColumnConfig);
33
+ var columnConfig = Ext.apply(defaultColumnConfig, field);
34
+
35
+ // apply dynamically defined properties
36
+ Ext.apply(columnConfig, {
37
+ fieldLabel: columnConfig.fieldLabel || columnConfig.name.humanize(),
38
+ hideLabel: columnConfig.hidden, // completely hide fields marked "hidden"
39
+ parentId: this.id,
40
+ name: columnConfig.name,
41
+ checked: columnConfig.xtype == "xcheckbox" ? columnConfig.value : null // checkbox state
42
+ });
43
+
44
+ this.items.push(columnConfig);
45
+ }
46
+ }, this);
47
+
48
+ var Record = Ext.data.Record.create(recordFields);
49
+ this.reader = new Ext.data.RecordArrayReader({root:"data"}, Record);
50
+
51
+ delete this.clmns; // we don't need them anymore
52
+
53
+ // Now let Ext.form.FormPanel do the rest
54
+ #{js_full_class_name}.superclass.initComponent.call(this);
55
+
56
+ // Apply event
57
+ this.addEvents('apply');
58
+ }
59
+ END_OF_JAVASCRIPT
60
+
61
+ # Defaults for each field
62
+ :defaults => {
63
+ :anchor => '-20', # to leave some space for the scrollbar
64
+ # :width => 180, # we do NOT want fixed size because it doesn't look nice when resizing
65
+ :listeners => {
66
+ # On "return" key, submit the form
67
+ :specialkey => {
68
+ :fn => <<-END_OF_JAVASCRIPT.l
69
+ function(field, event){
70
+ if (event.getKey() == 13) this.ownerCt.onApply();
71
+ }
72
+ END_OF_JAVASCRIPT
73
+ }
74
+ }
75
+ },
76
+
77
+ :default_column_config => config_columns.inject({}){ |r, c| r.merge!({
78
+ c[:name] => c[:default]
79
+ }) },
80
+
81
+ :set_form_values => <<-END_OF_JAVASCRIPT.l,
82
+ function(values){
83
+ this.form.loadRecord(this.reader.readRecords({data:[values]}).records[0]);
84
+ }
85
+ END_OF_JAVASCRIPT
86
+
87
+ :load_record => <<-END_OF_JAVASCRIPT.l,
88
+ function(id, neighbour){
89
+ this.netzkeLoad({id:id});
90
+ }
91
+ END_OF_JAVASCRIPT
92
+
93
+ # :previous => <<-END_OF_JAVASCRIPT.l,
94
+ # function() {
95
+ # var currentId = this.form.getValues().id;
96
+ # this.loadRecord(currentId, 'previous');
97
+ # }
98
+ # END_OF_JAVASCRIPT
99
+ #
100
+ # :next => <<-END_OF_JAVASCRIPT.l,
101
+ # function() {
102
+ # var currentId = this.form.getValues().id;
103
+ # this.loadRecord(currentId, 'next');
104
+ # }
105
+ # END_OF_JAVASCRIPT
106
+
107
+ :on_apply => <<-END_OF_JAVASCRIPT.l
108
+ function() {
109
+ if (this.fireEvent('apply', this)) {
110
+ var values = this.getForm().getValues();
111
+ for (var k in values) {
112
+ if (values[k] == "") {delete values[k]}
113
+ }
114
+ if (this.fileUpload) {
115
+ // Not a Netzke's standard API call, because the form is multipart
116
+ this.getForm().submit({
117
+ url: this.buildApiUrl("netzke_submit"),
118
+ params: {
119
+ data: Ext.encode(values)
120
+ },
121
+ failure: function(form, action){
122
+ // It will always be failure, as we don't play along with the Ext success indication (not returning {success: true})
123
+ this.bulkExecute(Ext.decode(action.response.responseText));
124
+ this.fireEvent('submitsuccess');
125
+ },
126
+ scope: this
127
+ });
128
+ } else {
129
+ // Submit the data and process the result
130
+ this.netzkeSubmit(Ext.apply((this.baseParams || {}), {data:Ext.encode(values)}), function(result){
131
+ if (result === "ok") {this.fireEvent("submitsuccess")};
132
+ }, this);
133
+ }
134
+ }
135
+ }
136
+ END_OF_JAVASCRIPT
137
+ }
138
+ end
139
+
140
+ end
141
+ end
142
+ end
143
+ end