netzke-basepack 0.5.12 → 0.5.13

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.
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