netzke-basepack 0.5.12 → 0.5.13

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,16 @@
1
+ = v0.5.13 - 2010-08-11
2
+ * regression
3
+ * combobox options configuration again has effect
4
+
5
+ * bug fix
6
+ * when a TabPanel was used as a standalone widget, the first tab was rendered empty
7
+ * Grid/FormPanel: dynamically changing of columns doesn't erase those column settings that are not configurable via GUI, but which were specified in the code
8
+
9
+ * enhancements
10
+ * scopes can now be specified for an association combobox in Grid/FormPanels
11
+ * GridPanel: you can now configure add/edit/multi_edit/search panels (e.g. to override the fields) and corresponding windows (e.g. to override the title)
12
+ * minor refactoring GridPanel: moved static js out of grid_panel_js.rb
13
+
1
14
  = v0.5.12 - 2010-06-21
2
15
  * Fix: when used with Bundler, was crashing with the "uninitialized constant" exception
3
16
 
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  begin
2
2
  require 'jeweler'
3
3
  Jeweler::Tasks.new do |gemspec|
4
- gemspec.version = "0.5.12"
4
+ gemspec.version = "0.5.13"
5
5
  gemspec.name = "netzke-basepack"
6
6
  gemspec.summary = "Pre-built Rails + ExtJS widgets for your RIA"
7
7
  gemspec.description = "A set of full-featured extendible Netzke widgets (such as FormPanel, GridPanel, Window, BorderLayoutPanel, etc) which can be used as building block for your RIA"
@@ -9,7 +9,7 @@ begin
9
9
  gemspec.homepage = "http://github.com/skozlov/netzke-basepack"
10
10
  gemspec.rubyforge_project = "netzke-basepack"
11
11
  gemspec.authors = ["Sergei Kozlov"]
12
- gemspec.add_dependency("netzke-core", ">=0.5.3")
12
+ gemspec.add_dependency("netzke-core", ">=0.5.4")
13
13
  gemspec.add_dependency("searchlogic", ">=2.0.0")
14
14
  gemspec.add_dependency("will_paginate", ">=2.0.0")
15
15
  gemspec.add_dependency("acts_as_list", ">=0.1.2")
data/TODO.rdoc CHANGED
@@ -1,6 +1,10 @@
1
1
  == Priority
2
2
  * Add icons to buttons to actions (toolbars/menus)
3
3
  * On grid refresh, reset the dirty fields, so that the "Apply" button doesn't do anything
4
+ * GridPanel's read_only vs editable (doesn't work now) options
5
+ * Solve the confusion about columns being stored in the persistent config
6
+ * Inclusion of css in a stand-alone widget
7
+ * Rename the "l" method to something else
4
8
 
5
9
  == Foolproof
6
10
  * Should not be possible delete the "ID" field from grids/forms
@@ -41,7 +41,7 @@ class NetzkePersistentArrayAutoModel < ActiveRecord::Base
41
41
  # only select those attributes that were provided to us as columns. The rest is ignored.
42
42
  column_names = config[:columns].map{ |c| c[:name] }
43
43
  clean_data = data.collect{ |c| c.reject{ |k,v| !column_names.include?(k.to_s) } }
44
-
44
+ Rails.logger.debug "!!! clean_data: #{clean_data.inspect}\n"
45
45
  self.delete_all
46
46
  self.create(clean_data)
47
47
  end
@@ -56,7 +56,7 @@ module Netzke::ActiveRecord::Attributes
56
56
  in_columns_hash = columns_hash[attr_name] && {:name => attr_name, :attr_type => columns_hash[attr_name].type, :default_value => columns_hash[attr_name].default} || {} # {:virtual => true} # if nothing found in columns, mark it as "virtual" or not?
57
57
  if in_columns_hash.empty?
58
58
  # If not among the model columns, it's either virtual, or an association
59
- merged = association_attr?(attr_name) ? {:name => attr_name} : declared.merge(:virtual => true)
59
+ merged = association_attr?(attr_name) ? declared.merge!(:name => attr_name) : declared.merge(:virtual => true)
60
60
  else
61
61
  # .. otherwise merge with what's declared
62
62
  merged = in_columns_hash.merge(declared)
@@ -1,43 +1,13 @@
1
1
  module Netzke::ActiveRecord::ComboboxOptions
2
2
  module ClassMethods
3
- # TODO: rename to netzke_options_for (to avoid polluting the namespace)
3
+ # TODO: rename to netzke_options_for (to avoid naming conflicts)
4
4
  def options_for(column, query = "")
5
- # First, check if we have options for this class and column defined in persistent storage
6
- # NetzkePreference.widget_name = self.name
7
- # options = NetzkePreference[:combobox_options] || {}
8
- # if options[column]
9
- # options[column].select{ |o| o.index(/^#{query}/) }
10
- if respond_to?("#{column}_combobox_options")
11
- # AR model provides the choices itself
12
- send("#{column}_combobox_options", query)
13
- else
14
- # Returns all unique values for a column, filtered with <tt>query</tt>
15
- if (assoc_name, *assoc_method = column.split('__')).size > 1
16
- # column is an association column
17
- assoc_method = assoc_method.join('__') # in case we get something like country__continent__name
18
- association = reflect_on_association(assoc_name.to_sym) || raise(NameError, "Association #{assoc_name} not known for class #{name}")
19
- association.klass.options_for(assoc_method, query)
20
- else
21
- column = assoc_name
22
- if self.column_names.include?(column)
23
- # it's simply a column in the table
24
- records = query.empty? ? find_by_sql("select distinct #{column} from #{table_name}") : find_by_sql("select distinct #{column} from #{table_name} where #{column} like '#{query}%'")
25
- records.map{|r| r.send(column)}
26
- else
27
- # it's a "virtual" column - the least effective search
28
- records = self.find(:all).map{|r| r.send(column)}.uniq
29
- query.empty? ? records : records.select{|r| r.index(/^#{query}/)}
30
- end
31
- end
32
- end
5
+ records = query.empty? ? find_by_sql("select distinct #{column} from #{table_name}") : find_by_sql("select distinct #{column} from #{table_name} where #{column} like '#{query}%'")
6
+ records.map{|r| r.send(column)}
33
7
  end
34
8
  end
35
9
 
36
- module InstanceMethods
37
- end
38
-
39
10
  def self.included(receiver)
40
11
  receiver.extend ClassMethods
41
- receiver.send :include, InstanceMethods
42
12
  end
43
13
  end
@@ -1,15 +1,10 @@
1
1
  require "netzke/active_record/data_accessor"
2
+ require "searchlogic"
2
3
 
3
4
  module Netzke
4
- # This module is included into such data-driven widgets as GridPanel, FormPanel, etc. It provides for
5
- # flexible pre-configuration of (virtual) attributes.
6
- #
7
- # TODO: show examples of how to create the helpers
5
+ # This module is included into such data-driven widgets as GridPanel, FormPanel, etc.
8
6
  module DataAccessor
9
-
10
- # This method should be called from the constructor of the widget. It dynamically includes:
11
- # 1) helpers into the data model for this widget; those may contain instance methods used as virtual attributes
12
- # 2) generic (used by all "data accessor" widgets) extensions into the data model for this widget
7
+ # This method should be called from the constructor of the widget.
13
8
  def apply_helpers
14
9
  # Generic extensions to the data model
15
10
  if data_class # because some widgets, like FormPanel, may have it optional
@@ -17,6 +12,41 @@ module Netzke
17
12
  end
18
13
  end
19
14
 
15
+ # Returns options for comboboxes in grids/forms
16
+ def combobox_options_for_column(column, method_options = {})
17
+ # First, check if we have options for this column defined in persistent storage
18
+ options = column[:combobox_options] && column[:combobox_options].split("\n")
19
+ if options
20
+ (method_options[:query].nil? ? options : options.select{ |o| o.index(/^#{method_options[:query]}/) }).map{ |el| [el] }
21
+ else
22
+ assoc, assoc_method = assoc_and_assoc_method_for_column(column)
23
+
24
+ if assoc
25
+ # Options for an asssociation attribute
26
+
27
+ search = assoc.klass.searchlogic
28
+
29
+ # apply scopes
30
+ method_options[:scopes] && method_options[:scopes].each do |s|
31
+ if s.is_a?(Array)
32
+ scope_name, *args = s
33
+ search.send(scope_name, *args)
34
+ else
35
+ search.send(s, true)
36
+ end
37
+ end
38
+
39
+ # apply query
40
+ search.send("#{assoc_method}_like", "#{method_options[:query]}%") if method_options[:query]
41
+
42
+ search.all.map{ |r| [r.send(assoc_method)] }
43
+ else
44
+ # Options for a non-association attribute
45
+ data_class.options_for(column[:name], method_options[:query]).map{|s| [s]}
46
+ end
47
+ end
48
+ end
49
+
20
50
  # [:col1, "col2", {:name => :col3}] =>
21
51
  # [{:name => "col1"}, {:name => "col2"}, {:name => "col3"}]
22
52
  def normalize_attr_config(cols)
@@ -24,26 +54,16 @@ module Netzke
24
54
  c.is_a?(Symbol) || c.is_a?(String) ? {:name => c.to_s} : c.merge(:name => c[:name].to_s)
25
55
  end
26
56
  end
27
-
28
- # Make sure we have keys as symbols, not strings
29
- def normalize_array_of_columns(arry)
30
- arry.map do |f|
31
- if f.is_a?(Hash)
32
- f.symbolize_keys
33
- else
34
- f.to_sym
35
- end
36
- end
37
- end
38
57
 
39
- # From symbol to config hash
40
- def normalize_column(field)
41
- field.is_a?(Symbol) ? {:name => field.to_s} : field
58
+ # Returns association and association method for a column
59
+ def assoc_and_assoc_method_for_column(c)
60
+ assoc_name, assoc_method = c[:name].split('__')
61
+ assoc = data_class.reflect_on_association(assoc_name.to_sym) if assoc_method
62
+ [assoc, assoc_method]
42
63
  end
43
-
44
- # From symbols to config hashes
45
- def normalize_columns(items)
46
- items.map{|c| normalize_column(c)}
64
+
65
+ def association_attr?(name)
66
+ !!name.to_s.index("__")
47
67
  end
48
68
 
49
69
  end
@@ -65,15 +65,21 @@ module Netzke
65
65
  apply_helpers
66
66
  end
67
67
 
68
+ # Model class
68
69
  # (We can't memoize this method because at some point we extend it, e.g. in Netzke::DataAccessor)
69
70
  def data_class
70
71
  @data_class ||= begin
71
72
  klass = "Netzke::ModelExtensions::#{config[:model]}For#{short_widget_class_name}".constantize rescue nil
72
- klass || begin
73
- ::ActiveSupport::Deprecation.warn("data_class_name option is deprecated. Use model instead", caller) if config[:data_class_name]
74
- model_name = config[:model] || config[:data_class_name]
75
- model_name && model_name.constantize
76
- end
73
+ klass || original_data_class
74
+ end
75
+ end
76
+
77
+ # Model class before model extensions are taken into account
78
+ def original_data_class
79
+ @original_data_class ||= begin
80
+ ::ActiveSupport::Deprecation.warn("data_class_name option is deprecated. Use model instead", caller) if config[:data_class_name]
81
+ model_name = config[:model] || config[:data_class_name]
82
+ model_name && model_name.constantize
77
83
  end
78
84
  end
79
85
 
@@ -59,8 +59,12 @@ module Netzke
59
59
  def get_combobox_options(params)
60
60
  column = params[:column]
61
61
  query = params[:query]
62
+
63
+ column = fields.detect{ |c| c[:name] == params[:column] }.try(:to_options!)
64
+ scopes = column.to_options[:scopes]
65
+ query = params[:query]
62
66
 
63
- {:data => data_class.options_for(column, query).map{|s| [s]}}
67
+ {:data => combobox_options_for_column(column, :query => query, :scopes => scopes)}
64
68
  end
65
69
 
66
70
  def configuration_panel__fields__get_combobox_options(params)
@@ -14,71 +14,70 @@ module Netzke
14
14
  end
15
15
  end
16
16
 
17
- module InstanceMethods
18
- def fields
19
- @fields ||= begin
20
- flds = load_fields
21
- flds ||= initial_fields
22
-
23
- flds.map! do |c|
24
- value = record.send(c[:name])
25
- value.nil? ? c : c.merge(:value => value)
26
- end if record
27
-
28
- flds
29
- end
17
+ def fields
18
+ @fields ||= begin
19
+ flds = load_fields
20
+ reverse_merge_equally_named_fields(flds, initial_fields) if flds
21
+ flds ||= initial_fields
22
+
23
+ flds.map! do |c|
24
+ value = record.send(c[:name])
25
+ value.nil? ? c : c.merge(:value => value)
26
+ end if record
27
+
28
+ flds
30
29
  end
30
+ end
31
31
 
32
- def default_fields
33
- @default_fields ||= load_model_level_attrs || (data_class && data_class.netzke_attributes) || []
34
- end
32
+ def default_fields
33
+ @default_fields ||= load_model_level_attrs || (data_class && data_class.netzke_attributes) || []
34
+ end
35
35
 
36
- def initial_fields(only_included = true)
37
- ::ActiveSupport::Deprecation.warn("The :columns option for FormPanel is deprecated. Use :fields instead", caller) if config[:columns]
38
-
39
- # Normalize here, as from the config we can get symbols (names) instead of hashes
40
- fields_from_config = (config[:columns] || config[:fields]) && normalize_attr_config(config[:columns] || config[:fields])
36
+ def initial_fields(only_included = true)
37
+ ::ActiveSupport::Deprecation.warn("The :columns option for FormPanel is deprecated. Use :fields instead", caller) if config[:columns]
38
+
39
+ # Normalize here, as from the config we can get symbols (names) instead of hashes
40
+ fields_from_config = (config[:columns] || config[:fields]) && normalize_attr_config(config[:columns] || config[:fields])
41
41
 
42
- if fields_from_config
43
- # reverse-merge each column hash from config with each column hash from exposed_attributes (fields from config have higher priority)
44
- for c in fields_from_config
45
- corresponding_exposed_column = default_fields.find{ |k| k[:name] == c[:name] }
46
- c.reverse_merge!(corresponding_exposed_column) if corresponding_exposed_column
47
- end
48
- fields_for_create = fields_from_config
49
- elsif default_fields
50
- # we didn't have fields configured in widget's config, so, use the fields from the data class
51
- fields_for_create = default_fields
52
- else
53
- raise ArgumentError, "No fields specified for widget '#{global_id}'"
42
+ if fields_from_config
43
+ # reverse-merge each column hash from config with each column hash from exposed_attributes (fields from config have higher priority)
44
+ for c in fields_from_config
45
+ corresponding_exposed_column = default_fields.find{ |k| k[:name] == c[:name] }
46
+ c.reverse_merge!(corresponding_exposed_column) if corresponding_exposed_column
54
47
  end
55
-
56
- fields_for_create.reject!{ |c| c[:included] == false }
57
-
58
- fields_for_create.map! do |c|
59
- if data_class
60
-
61
- detect_association_with_method(c)
62
-
63
- # detect association column (e.g. :category_id)
64
- if assoc = data_class.reflect_on_all_associations.detect{|a| a.primary_key_name == c[:name]}
65
- c[:xtype] ||= xtype_for_association
66
- assoc_method = %w{name title label id}.detect{|m| (assoc.klass.instance_methods + assoc.klass.column_names).include?(m) } || assoc.klass.primary_key
67
- c[:name] = "#{assoc.name}__#{assoc_method}"
68
- end
48
+ fields_for_create = fields_from_config
49
+ elsif default_fields
50
+ # we didn't have fields configured in widget's config, so, use the fields from the data class
51
+ fields_for_create = default_fields
52
+ else
53
+ raise ArgumentError, "No fields specified for widget '#{global_id}'"
54
+ end
55
+
56
+ fields_for_create.reject!{ |c| c[:included] == false }
57
+
58
+ fields_for_create.map! do |c|
59
+ if data_class
69
60
 
70
- c[:hidden] = true if c[:name] == data_class.primary_key && c[:hidden].nil? # hide ID column by default
61
+ detect_association_with_method(c)
62
+
63
+ # detect association column (e.g. :category_id)
64
+ if assoc = data_class.reflect_on_all_associations.detect{|a| a.primary_key_name == c[:name]}
65
+ c[:xtype] ||= xtype_for_association
66
+ assoc_method = %w{name title label id}.detect{|m| (assoc.klass.instance_methods + assoc.klass.column_names).include?(m) } || assoc.klass.primary_key
67
+ c[:name] = "#{assoc.name}__#{assoc_method}"
71
68
  end
72
69
 
73
- set_default_field_label(c)
74
-
75
- c[:xtype] ||= xtype_for_attr_type(c[:attr_type]) # unless xtype_map[type].nil?
76
- c
70
+ c[:hidden] = true if c[:name] == data_class.primary_key && c[:hidden].nil? # hide ID column by default
77
71
  end
78
72
 
79
- fields_for_create
80
-
73
+ set_default_field_label(c)
74
+
75
+ c[:xtype] ||= xtype_for_attr_type(c[:attr_type]) # unless xtype_map[type].nil?
76
+ c
81
77
  end
78
+
79
+ fields_for_create
80
+
82
81
  end
83
82
 
84
83
  private
@@ -130,13 +129,16 @@ module Netzke
130
129
  end
131
130
  end
132
131
  end
133
-
134
132
  end
135
133
 
134
+ # Receives 2 arrays of columns. Merges the missing config from the +source+ into +dest+, matching columns by name
135
+ def reverse_merge_equally_named_fields(dest, source)
136
+ dest.each{ |dc| dc.reverse_merge!(source.detect{ |sc| sc[:name] == dc[:name] }) }
137
+ end
138
+
136
139
 
137
140
  def self.included(receiver)
138
141
  receiver.extend ClassMethods
139
- receiver.send :include, InstanceMethods
140
142
 
141
143
  receiver.class_eval do
142
144
  alias :initial_columns :initial_fields
@@ -44,7 +44,7 @@ module Netzke
44
44
  # * <tt>:strong_default_attrs</tt> - a hash of attributes to be merged atop of every created/updated record.
45
45
  # * <tt>:scopes</tt> - an array of named scopes to filter grid data, e.g.:
46
46
  #
47
- # [["user_id_not", 100], ["name_like", "Peter"]]
47
+ # [["user_id_not", 100], ["name_like", "Peter"], :recent]
48
48
  #
49
49
  # In the <tt>:ext_config</tt> hash (see Netzke::Base) the following GridPanel specific options are available:
50
50
  #
@@ -57,10 +57,12 @@ module Netzke
57
57
  # * <tt>:rows_per_page</tt> - number of rows per page (ignored when <tt>:enable_pagination</tt> is set to <tt>false</tt>)
58
58
  # * <tt>:load_inline_data</tt> - load initial data into the grid right after its instantiation (saves a request to server); defaults to <tt>true</tt>
59
59
  # * <tt>:mode</tt> - when set to <tt>:config</tt>, GridPanel loads in configuration mode
60
+ # * <tt>:add/edit/multi_edit/search_form_config</tt> - additional configuration for add/edit/multi_edit/search form panel
61
+ # * <tt>:add/edit/multi_edit_form_window_config</tt> - additional configuration for the window that wrapps up add/edit/multi_edit form panel
60
62
  #
61
63
  # Additionally supports Netzke::Base config options.
62
64
  #
63
- # == Column
65
+ # == Columns
64
66
  # Here's how the GridPanel decides which columns in which sequence and with which configuration to display.
65
67
  # First, the column configs are aquired from this GridPanel's persistent storage, as an array of hashes, each
66
68
  # representing a column configuration, such as:
@@ -75,7 +77,7 @@ module Netzke
75
77
  # ... which in its turn overrides the defaults provided by persistent storage managed by the AttributesConfigurator
76
78
  # that provides *model-level* (as opposed to a widget-level) configuration of a database model
77
79
  # (which is used by both grids and forms in Netzke).
78
- # And lastly, the defaults for AttributesConfigurator are calculated from the database model itself, powered by Netzke.
80
+ # And lastly, the defaults for AttributesConfigurator are calculated from the database model itself (extended by Netzke).
79
81
  # For example, in the model you can specify virtual attributes and their types that will be picked up by Netzke, the default
80
82
  # order of columns, or excluded columns. For details see <tt>Netzke::ActiveRecord::Attributes</tt>.
81
83
  #
@@ -97,7 +99,6 @@ module Netzke
97
99
 
98
100
  # Code shared between GridPanel, FormPanel, and other widgets that serve as interface to database tables
99
101
  include Netzke::DataAccessor
100
-
101
102
 
102
103
  def self.enforce_config_consistency
103
104
  config[:default_config][:ext_config][:enable_edit_in_form] &&= config[:edit_in_form_available]
@@ -139,7 +140,13 @@ module Netzke
139
140
 
140
141
  # Include extra javascript that we depend on
141
142
  def self.include_js
142
- res = []
143
+ res = ["#{File.dirname(__FILE__)}/grid_panel/javascripts/grid_panel_pre.js"]
144
+
145
+ # Optional edit in form functionality
146
+ res << "#{File.dirname(__FILE__)}/grid_panel/javascripts/edit_in_form.js" if config[:edit_in_form_available]
147
+
148
+ # Optional extended search functionality
149
+ res << "#{File.dirname(__FILE__)}/grid_panel/javascripts/advanced_search.js" if config[:extended_search_available]
143
150
 
144
151
  # Checkcolumn
145
152
  ext_examples = Netzke::Base.config[:ext_location] + "/examples/"
@@ -173,53 +180,42 @@ module Netzke
173
180
  # Edit in form
174
181
  api :create_new_record if config[:edit_in_form_available]
175
182
 
183
+ # Model class
176
184
  # (We can't memoize this method because at some point we extend it, e.g. in Netzke::DataAccessor)
177
185
  def data_class
178
186
  @data_class ||= begin
179
187
  klass = "Netzke::ModelExtensions::#{config[:model]}For#{short_widget_class_name}".constantize rescue nil
180
- klass || begin
181
- ::ActiveSupport::Deprecation.warn("data_class_name option is deprecated. Use model instead", caller) if config[:data_class_name]
182
- model_name = config[:model] || config[:data_class_name]
183
- model_name.nil? ? raise(ArgumentError, "No model specified for widget #{global_id}") : model_name.constantize
184
- end
188
+ klass || original_data_class
185
189
  end
186
190
  end
187
191
 
192
+ # Model class before model extensions are taken into account
193
+ def original_data_class
194
+ @original_data_class ||= begin
195
+ ::ActiveSupport::Deprecation.warn("data_class_name option is deprecated. Use model instead", caller) if config[:data_class_name]
196
+ model_name = config[:model] || config[:data_class_name]
197
+ model_name.nil? ? raise(ArgumentError, "No model specified for widget #{global_id}") : model_name.constantize
198
+ end
199
+ end
200
+
188
201
  def initialize(config = {}, parent = nil)
189
202
  super
190
-
191
203
  apply_helpers
192
204
  end
193
205
 
194
- # def data_class
195
- # klass = "Netzke::ModelExtensions::#{data_class.name}#{short_widget_class_name}Ext".constantize rescue nil
196
- # klass || data_class
197
- # end
198
-
199
206
  # Fields to be displayed in the "General" tab of the configuration panel
200
207
  def self.property_fields
201
- res = [
208
+ [
202
209
  {:name => :ext_config__title, :type => :string},
203
210
  {:name => :ext_config__header, :type => :boolean, :default => true},
204
211
  {:name => :ext_config__enable_context_menu, :type => :boolean, :default => true},
205
- # {:name => :ext_config__context_menu, :type => :json},
206
212
  {:name => :ext_config__enable_pagination, :type => :boolean, :default => true},
207
213
  {:name => :ext_config__rows_per_page, :type => :integer},
208
- # {:name => :ext_config__bbar, :type => :json},
209
214
  {:name => :ext_config__prohibit_create, :type => :boolean},
210
215
  {:name => :ext_config__prohibit_update, :type => :boolean},
211
216
  {:name => :ext_config__prohibit_delete, :type => :boolean},
212
217
  {:name => :ext_config__prohibit_read, :type => :boolean}
213
218
  ]
214
-
215
- # res << {:name => :ext_config__enable_extended_search, :type => :boolean} if config[:extended_search_available]
216
- # res << {:name => :ext_config__enable_edit_in_form, :type => :boolean} if config[:edit_in_form_available]
217
-
218
- # TODO: a buggy thing
219
- # res << {:name => :layout__columns, :type => :json}
220
-
221
- res
222
-
223
219
  end
224
220
 
225
221
  def default_config
@@ -298,7 +294,7 @@ module Netzke
298
294
  :add_form => {
299
295
  :class_name => "GridPanel::RecordFormWindow",
300
296
  :ext_config => {
301
- :title => "Add #{data_class.name.humanize}",
297
+ :title => "Add #{data_class.table_name.singularize.humanize}",
302
298
  :button_align => "right"
303
299
  },
304
300
  :item => {
@@ -314,13 +310,13 @@ module Netzke
314
310
  :mode => ext_config[:mode]
315
311
  },
316
312
  :record => data_class.new
317
- }
318
- },
313
+ }.deep_merge(config[:add_form_config] || {})
314
+ }.deep_merge(config[:add_form_window_config] || {}),
319
315
 
320
316
  :edit_form => {
321
317
  :class_name => "GridPanel::RecordFormWindow",
322
318
  :ext_config => {
323
- :title => "Edit #{data_class.name.humanize}",
319
+ :title => "Edit #{data_class.table_name.singularize.humanize}",
324
320
  :button_align => "right"
325
321
  },
326
322
  :item => {
@@ -333,13 +329,13 @@ module Netzke
333
329
  :header => false,
334
330
  :mode => ext_config[:mode]
335
331
  }
336
- },
337
- },
332
+ }.deep_merge(config[:edit_form_config] || {})
333
+ }.deep_merge(config[:edit_form_window_config] || {}),
338
334
 
339
335
  :multi_edit_form => {
340
336
  :class_name => "GridPanel::RecordFormWindow",
341
337
  :ext_config => {
342
- :title => "Edit #{data_class.name.humanize}",
338
+ :title => "Edit #{data_class.table_name.humanize}",
343
339
  :button_align => "right"
344
340
  },
345
341
  :item => {
@@ -352,8 +348,8 @@ module Netzke
352
348
  :header => false,
353
349
  :mode => ext_config[:mode]
354
350
  }
355
- }
356
- }
351
+ }.deep_merge(config[:multi_edit_form_config] || {})
352
+ }.deep_merge(config[:multi_edit_form_window_config] || {})
357
353
  }) if ext_config[:enable_edit_in_form]
358
354
 
359
355
  # Extended search
@@ -368,7 +364,7 @@ module Netzke
368
364
  :bbar => false,
369
365
  :mode => ext_config[:mode]
370
366
  },
371
- }
367
+ }.deep_merge(config[:search_form_config] || {})
372
368
  }) if ext_config[:enable_extended_search]
373
369
 
374
370
  res