netzke-basepack 0.5.5.1 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
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