netzke-basepack 0.5.8 → 0.5.9

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/CHANGELOG.rdoc +18 -0
  2. data/README.rdoc +11 -4
  3. data/Rakefile +19 -3
  4. data/TODO.rdoc +1 -1
  5. data/generators/netzke_basepack/netzke_basepack_generator.rb +13 -0
  6. data/generators/netzke_basepack/templates/create_netzke_field_lists.rb +18 -0
  7. data/generators/netzke_basepack/templates/public_assets/ts-checkbox.gif +0 -0
  8. data/install.rb +1 -1
  9. data/javascripts/basepack.js +124 -30
  10. data/lib/app/models/netzke_field_list.rb +261 -0
  11. data/lib/app/models/netzke_model_attr_list.rb +21 -0
  12. data/lib/app/models/netzke_persistent_array_auto_model.rb +58 -0
  13. data/lib/netzke-basepack.rb +17 -2
  14. data/lib/netzke/active_record.rb +10 -0
  15. data/lib/netzke/active_record/association_attributes.rb +102 -0
  16. data/lib/netzke/active_record/attributes.rb +100 -0
  17. data/lib/netzke/active_record/combobox_options.rb +43 -0
  18. data/lib/netzke/active_record/data_accessor.rb +9 -12
  19. data/lib/netzke/attributes_configurator.rb +195 -0
  20. data/lib/netzke/basic_app.rb +47 -4
  21. data/lib/netzke/configuration_panel.rb +1 -1
  22. data/lib/netzke/data_accessor.rb +7 -30
  23. data/lib/netzke/fields_configurator.rb +106 -41
  24. data/lib/netzke/form_panel.rb +28 -125
  25. data/lib/netzke/form_panel/form_panel_api.rb +2 -3
  26. data/lib/netzke/form_panel/form_panel_fields.rb +147 -0
  27. data/lib/netzke/form_panel/form_panel_js.rb +35 -15
  28. data/lib/netzke/grid_panel.rb +130 -213
  29. data/lib/netzke/grid_panel/grid_panel_api.rb +254 -257
  30. data/lib/netzke/grid_panel/grid_panel_columns.rb +226 -0
  31. data/lib/netzke/grid_panel/grid_panel_js.rb +126 -119
  32. data/lib/netzke/grid_panel/record_form_window.rb +7 -1
  33. data/lib/netzke/json_array_editor.rb +61 -0
  34. data/lib/netzke/plugins/configuration_tool.rb +1 -1
  35. data/lib/netzke/property_editor.rb +3 -3
  36. data/lib/netzke/search_panel.rb +164 -27
  37. data/lib/netzke/tab_panel.rb +14 -12
  38. data/stylesheets/basepack.css +43 -2
  39. data/test/app_root/app/models/book.rb +1 -1
  40. data/test/app_root/app/models/role.rb +3 -0
  41. data/test/app_root/app/models/user.rb +3 -0
  42. data/test/app_root/config/database.yml +1 -1
  43. data/test/app_root/db/migrate/20090102223630_create_netzke_field_lists.rb +18 -0
  44. data/test/app_root/db/migrate/20090423214303_create_roles.rb +11 -0
  45. data/test/app_root/db/migrate/20090423222114_create_users.rb +12 -0
  46. data/test/fixtures/books.yml +4 -2
  47. data/test/fixtures/categories.yml +2 -2
  48. data/test/fixtures/genres.yml +6 -6
  49. data/test/fixtures/roles.yml +8 -0
  50. data/test/fixtures/users.yml +11 -0
  51. data/test/test_helper.rb +2 -0
  52. data/test/unit/active_record_basepack_test.rb +2 -2
  53. data/test/unit/fields_configuration_test.rb +18 -0
  54. data/test/unit/grid_panel_test.rb +29 -27
  55. metadata +41 -16
  56. data/lib/app/models/netzke_auto_column.rb +0 -4
  57. data/lib/app/models/netzke_auto_field.rb +0 -4
  58. data/lib/app/models/netzke_auto_table.rb +0 -61
  59. data/lib/netzke/active_record/basepack.rb +0 -134
  60. data/lib/netzke/grid_panel/javascripts/filters.js +0 -7
  61. data/test/app_root/db/migrate/20090102223630_create_netzke_layouts.rb +0 -14
  62. data/test/unit/helper_model_test.rb +0 -30
@@ -0,0 +1,21 @@
1
+ # Holds attribute lists for application models.
2
+ # Is used to configure attributes in the layer between a model and its representation in the Netzke application, thus providing default attributes
3
+ # for grids and panels.
4
+ class NetzkeModelAttrList < NetzkeFieldList
5
+
6
+ # Updates attributes for all lists owned by owner_id and below the current authority level
7
+ def self.update_fields(owner_id, attrs_hash)
8
+ super
9
+
10
+ NetzkeFieldList.find_all_lists_under_current_authority(owner_id).each do |list|
11
+ list.update_attrs(attrs_hash)
12
+ end
13
+ end
14
+
15
+ def self.add_attrs(attrs)
16
+ NetzkeFieldList.find_all_lists_under_current_authority(owner_id).each do |list|
17
+ attrs.each{ |attr_hash| list.append_attr(attr_hash) }
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,58 @@
1
+ require 'acts_as_list'
2
+ class NetzkePersistentArrayAutoModel < ActiveRecord::Base
3
+ set_table_name "netzke_temp_table"
4
+ connection.create_table(table_name){} if !connection.table_exists?(table_name)
5
+
6
+ acts_as_list
7
+ default_scope :order => "position"
8
+
9
+ cattr_accessor :config
10
+
11
+ def self.all_columns
12
+ self.all.map{ |c| c.attributes.reject{ |k,v| k == 'id' || k == 'position' } }
13
+ end
14
+
15
+ # Configuration
16
+ def self.configure(config)
17
+ self.config = config
18
+ if NetzkePreference.first(:conditions => {:name => "netzke_persistent_array_refresh_token"}).try(:value) != refresh_token || !connection.table_exists?(table_name)
19
+ rebuild_table#(:columns => config[:columns], :initial_data => config[:initial_data])
20
+ end
21
+ NetzkePreference.find_or_create_by_name("netzke_persistent_array_refresh_token").update_attribute(:value, refresh_token)
22
+ end
23
+
24
+ def self.rebuild_table#(config)
25
+ connection.drop_table(table_name) if connection.table_exists?(table_name)
26
+ # create the table with the fields
27
+ self.connection.create_table(table_name) do |t|
28
+ config[:columns].each do |c|
29
+ c = c.dup # to make next line shorter
30
+ t.column c.delete(:name), c.delete(:type), c
31
+ end
32
+ end
33
+
34
+ self.reset_column_information
35
+
36
+ # self.create config[:initial_data]
37
+ self.replace_data(config[:initial_data])
38
+ end
39
+
40
+ def self.replace_data(data)
41
+ # only select those attributes that were provided to us as columns. The rest is ignored.
42
+ column_names = config[:columns].map{ |c| c[:name] }
43
+ clean_data = data.collect{ |c| c.reject{ |k,v| !column_names.include?(k.to_s) } }
44
+
45
+ self.delete_all
46
+ self.create(clean_data)
47
+ end
48
+
49
+ private
50
+
51
+ def self.refresh_token
52
+ @@refresh_token ||= begin
53
+ session = Netzke::Base.session
54
+ config[:owner] + (session[:masq_user] || session[:masq_role] || session[:masq_world] || session[:netzke_user_id]).to_s
55
+ end
56
+ end
57
+
58
+ end
@@ -4,6 +4,7 @@ require 'netzke-core'
4
4
 
5
5
  # ExtJS-related constants
6
6
  require 'netzke/ext'
7
+ require 'netzke/active_record'
7
8
 
8
9
  # Make widget classes auto-loadable with help of ActiveSupport
9
10
  path = File.dirname(__FILE__)
@@ -25,5 +26,19 @@ end
25
26
  Netzke::Base.config[:javascripts] << "#{File.dirname(__FILE__)}/../javascripts/basepack.js"
26
27
  Netzke::Base.config[:stylesheets] << "#{File.dirname(__FILE__)}/../stylesheets/basepack.css"
27
28
 
28
- # FIXME: doesn't belong here
29
- Netzke::Base.config[:stylesheets] << Netzke::Base.config[:ext_location] + "/examples/ux/fileuploadfield/css/fileuploadfield.css" if Netzke::Base.config[:ext_location]
29
+
30
+ # FIXME: The following stylesheet inclusion doesn't *really* belong here, being widget-specific,
31
+ # but I don't see any other solution for now. The problem is that these stylesheets come straight from
32
+ # Ext JS, having *relative* URLs to the images, which doesn't allow us to include them all together as those stylesheets
33
+ # from Netzke.
34
+
35
+ # Used by FormPanel (file upload field)
36
+ Netzke::Base.config[:external_css] << "/extjs/examples/ux/fileuploadfield/css/fileuploadfield"
37
+
38
+ # Used by GridPanel
39
+ Netzke::Base.config[:external_css] << "/extjs/examples/ux/gridfilters/css/RangeMenu"
40
+ Netzke::Base.config[:external_css] << "/extjs/examples/ux/gridfilters/css/GridFilters"
41
+
42
+ if Netzke::Base.config[:with_icons].nil? && defined?(RAILS_ROOT)
43
+ Netzke::Base.config[:with_icons] = File.exists?("#{RAILS_ROOT}/public#{Netzke::Base.config[:icons_uri]}")
44
+ end
@@ -0,0 +1,10 @@
1
+ module Netzke::ActiveRecord
2
+
3
+ # Extend ActiveRecord
4
+ ActiveRecord::Base.class_eval do
5
+ include AssociationAttributes
6
+ include Attributes
7
+ include ComboboxOptions
8
+ end
9
+
10
+ end
@@ -0,0 +1,102 @@
1
+ require "active_record"
2
+
3
+ module Netzke::ActiveRecord
4
+ # Provides extensions to all ActiveRecord-based classes
5
+ module AssociationAttributes
6
+ module ClassMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+ # Allow nested association access (assocs separated by "." or "__"), e.g.: proxy_service.asset__gui_folder__name
11
+ # Example:
12
+ #
13
+ # Book.first.genre__name = 'Fantasy'
14
+ #
15
+ # is the same as:
16
+ #
17
+ # Book.first.genre = Genre.find_by_name('Fantasy')
18
+ #
19
+ # The result - easier forms and grids that handle nested models: simply specify column/field name as "genre__name".
20
+ def method_missing_with_basepack(method, *args, &block)
21
+ # if refering to a column, just pass it to the original method_missing
22
+ return method_missing_without_basepack(method, *args, &block) if self.class.column_names.include?(method.to_s)
23
+
24
+ split = method.to_s.split(/\.|__/)
25
+ if split.size > 1
26
+ if split.last =~ /=$/
27
+ if split.size == 2
28
+ # search for association and assign it to self
29
+ assoc = self.class.reflect_on_association(split.first.to_sym)
30
+ assoc_method = split.last.chop
31
+ if assoc
32
+ begin
33
+ assoc_instance = assoc.klass.send("find_by_#{assoc_method}", *args)
34
+ rescue NoMethodError
35
+ assoc_instance = nil
36
+ logger.debug "!!! no find_by_#{assoc_method} method for class #{assoc.klass.name}\n"
37
+ end
38
+ if (assoc_instance)
39
+ self.send("#{split.first}=", assoc_instance)
40
+ else
41
+ logger.debug "!!! Couldn't find association #{split.first} by #{assoc_method} '#{args.first}'"
42
+ end
43
+ else
44
+ method_missing_without_basepack(method, *args, &block)
45
+ end
46
+ else
47
+ method_missing_without_basepack(method, *args, &block)
48
+ end
49
+ else
50
+ res = self
51
+ split.each do |m|
52
+ if res.respond_to?(m)
53
+ res = res.send(m) unless res.nil?
54
+ else
55
+ res.nil? ? nil : method_missing_without_basepack(method, *args, &block)
56
+ end
57
+ end
58
+ res
59
+ end
60
+ else
61
+ method_missing_without_basepack(method, *args, &block)
62
+ end
63
+ end
64
+
65
+ # Make respond_to? return true for association assignment method, like "genre__name="
66
+ def respond_to_with_basepack?(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
+ respond_to_without_basepack?(method, include_private)
78
+ end
79
+ else
80
+ respond_to_without_basepack?(method, include_private)
81
+ end
82
+ else
83
+ # self.respond_to?(split.first) ? self.send(split.first).respond_to?(split[1..-1].join("__")) : false
84
+ respond_to_without_basepack?(method, include_private)
85
+ end
86
+ else
87
+ respond_to_without_basepack?(method, include_private)
88
+ end
89
+ end
90
+ end
91
+
92
+ def self.included(receiver)
93
+ receiver.extend ClassMethods
94
+
95
+ receiver.send :include, InstanceMethods
96
+ receiver.alias_method_chain :method_missing, :basepack
97
+ receiver.alias_method_chain :respond_to?, :basepack
98
+ end
99
+
100
+ end
101
+ end
102
+
@@ -0,0 +1,100 @@
1
+ module Netzke::ActiveRecord::Attributes
2
+ module ClassMethods
3
+
4
+ # Define or configure an attribute.
5
+ # Example:
6
+ # netzke_attribute :recent, :type => :boolean, :read_only => true
7
+ def netzke_attribute(name, options = {})
8
+ name = name.to_s
9
+ options[:attr_type] = options.delete(:type) || :string
10
+ declared_attrs = read_inheritable_attribute(:netzke_declared_attributes) || []
11
+ # if the attr was declared already, simply merge it with the new options
12
+ existing = declared_attrs.detect{ |va| va[:name] == name }
13
+ if existing
14
+ existing.merge!(options)
15
+ else
16
+ declared_attrs << {:name => name}.merge(options)
17
+ end
18
+ write_inheritable_attribute(:netzke_declared_attributes, declared_attrs)
19
+ end
20
+
21
+ # Exclude attributes from being picked up by grids and forms.
22
+ # Accepts an array of attribute names (as symbols).
23
+ # Example:
24
+ # netzke_expose_attributes :created_at, :updated_at, :crypted_password
25
+ def netzke_exclude_attributes(*args)
26
+ write_inheritable_attribute(:netzke_excluded_attributes, args.map(&:to_s))
27
+ end
28
+
29
+ # Explicitly expose attributes that should be picked up by grids and forms.
30
+ # Accepts an array of attribute names (as symbols).
31
+ # Takes precedence over <tt>netzke_exclude_attributes</tt>.
32
+ # Example:
33
+ # netzke_expose_attributes :name, :role__name
34
+ def netzke_expose_attributes(*args)
35
+ write_inheritable_attribute(:netzke_exposed_attributes, args.map(&:to_s))
36
+ end
37
+
38
+ # Returns the attributes that will be picked up by grids and forms.
39
+ def netzke_attributes
40
+ exposed = read_inheritable_attribute(:netzke_exposed_attributes)
41
+ exposed ? netzke_attrs_in_forced_order(exposed) : netzke_attrs_in_natural_order
42
+ end
43
+
44
+ private
45
+ def netzke_declared_attributes
46
+ read_inheritable_attribute(:netzke_declared_attributes) || []
47
+ end
48
+
49
+ def netzke_excluded_attributes
50
+ read_inheritable_attribute(:netzke_excluded_attributes) || []
51
+ end
52
+
53
+ def netzke_attrs_in_forced_order(attrs)
54
+ attrs.collect do |attr_name|
55
+ declared = netzke_declared_attributes.detect { |va| va[:name] == attr_name } || {}
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
+ if in_columns_hash.empty?
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)
60
+ else
61
+ # .. otherwise merge with what's declared
62
+ merged = in_columns_hash.merge(declared)
63
+ end
64
+
65
+ # We didn't find it among declared, nor among the model columns, nor does it seem association attribute
66
+ merged[:name].nil? && raise(ArgumentError, "Unknown attribute '#{attr_name}' for model #{self.name}", caller)
67
+
68
+ merged
69
+ end
70
+ end
71
+
72
+ def netzke_attrs_in_natural_order
73
+ (
74
+ declared_attrs = netzke_declared_attributes
75
+ column_names.map do |name|
76
+ c = {:name => name, :attr_type => columns_hash[name].type}
77
+ # auto set up the default value from the column settings
78
+ c.merge!(:default_value => columns_hash[name].default) if columns_hash[name].default
79
+
80
+ # if there's a declared attr with the same name, simply merge it with what's taken from the model's columns
81
+ if declared = declared_attrs.detect{ |va| va[:name] == name }
82
+ c.merge!(declared)
83
+ declared_attrs.delete(declared)
84
+ end
85
+ c
86
+ end +
87
+ declared_attrs
88
+ ).reject { |attr| netzke_excluded_attributes.include?(attr[:name]) }
89
+ end
90
+
91
+ def association_attr?(attr_name)
92
+ !!attr_name.index("__") # probably we can't do much better than this, as we don't know at this moment if the associated model has a specific attribute, and we don't really want to find it out
93
+ end
94
+
95
+ end
96
+
97
+ def self.included(receiver)
98
+ receiver.extend ClassMethods
99
+ end
100
+ end
@@ -0,0 +1,43 @@
1
+ module Netzke::ActiveRecord::ComboboxOptions
2
+ module ClassMethods
3
+ # TODO: rename to netzke_options_for (to avoid polluting the namespace)
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
33
+ end
34
+ end
35
+
36
+ module InstanceMethods
37
+ end
38
+
39
+ def self.included(receiver)
40
+ receiver.extend ClassMethods
41
+ receiver.send :include, InstanceMethods
42
+ end
43
+ end
@@ -1,5 +1,3 @@
1
- require 'netzke/active_record/basepack'
2
-
3
1
  module Netzke::ActiveRecord
4
2
  # Provides extensions to those ActiveRecord-based models that provide data to the "data accessor" widgets,
5
3
  # like GridPanel, FormPanel, etc
@@ -7,22 +5,21 @@ module Netzke::ActiveRecord
7
5
 
8
6
  # Allow specify the netzke widget that requires this data. Virtual attributes may be using it to produce
9
7
  # widget-dependent result.
10
- def netzke_widget=(widget)
11
- @netzke_widget = widget
12
- end
13
-
14
- def netzke_widget
15
- @netzke_widget
16
- end
8
+ # def netzke_widget=(widget)
9
+ # @netzke_widget = widget
10
+ # end
11
+ #
12
+ # def netzke_widget
13
+ # @netzke_widget
14
+ # end
17
15
 
18
16
  # Transforms a record to array of values according to the passed columns.
19
17
  def to_array(columns, widget = nil)
20
- self.netzke_widget = widget
18
+ # self.netzke_widget = widget
21
19
  res = []
22
20
  for c in columns
23
- nc = c.is_a?(Symbol) ? {:name => c} : c
24
21
  begin
25
- res << send(nc[:name]) unless nc[:excluded]
22
+ res << send(c[:name]) unless c[:included] == false
26
23
  rescue
27
24
  # So that we don't crash at a badly configured column
28
25
  res << "UNDEF"
@@ -0,0 +1,195 @@
1
+ module Netzke
2
+ # == AttributesConfigurator
3
+ # Provides dynamic configuring of attributes for a specific model. This will be picked up by Grid/FormPanels as defaults.
4
+ # Configuration parameters:
5
+ # * <tt>:model</tt> - model to configure attributes for
6
+ class AttributesConfigurator < JsonArrayEditor
7
+ api :load_defaults
8
+
9
+ def default_columns
10
+ [{
11
+ :name => "id",
12
+ :attr_type => :integer
13
+ },{
14
+ :name => "included",
15
+ :attr_type => :boolean,
16
+ :default_value => true
17
+ },{
18
+ :name => "name",
19
+ :attr_type => :string,
20
+ :width => 200,
21
+ :editor => {
22
+ :xtype => :combo,
23
+ :store => config[:model].constantize.netzke_attributes.map{ |attr| attr[:name] },
24
+ :force_selection => true
25
+ }
26
+ },{
27
+ :name => "label",
28
+ :attr_type => :string,
29
+ :width => 200
30
+ },{
31
+ :name => "default_value",
32
+ :attr_type => :string,
33
+ :width => 200
34
+ },{
35
+ :name => "combobox_options",
36
+ :attr_type => :string,
37
+ :width => 200,
38
+ :editor => :textarea
39
+ },{
40
+ :name => "read_only",
41
+ :attr_type => :boolean,
42
+ :default_value => false,
43
+ :header => "R/O"
44
+ },{
45
+ :name => "position",
46
+ :attr_type => :integer,
47
+ :included => false
48
+ },{
49
+ :name => "attr_type",
50
+ :attr_type => :string,
51
+ :meta => :true
52
+ }]
53
+ end
54
+
55
+ def default_config
56
+ super.deep_merge({
57
+ :name => 'columns',
58
+ :ext_config => {
59
+ :header => false,
60
+ :enable_extended_search => false,
61
+ :enable_edit_in_form => false,
62
+ :enable_pagination => false,
63
+ :enable_rows_reordering => GridPanel.config[:rows_reordering_available]
64
+ }
65
+ })
66
+ end
67
+
68
+ def config_tool_needed?
69
+ false
70
+ end
71
+
72
+ def actions
73
+ super.merge(
74
+ :defaults => {:text => 'Restore defaults', :icon => Netzke::Base.config[:with_icons] && (Netzke::Base.config[:icons_uri] + "wand.png")}
75
+ )
76
+ end
77
+
78
+ def default_bbar
79
+ %w{ add edit apply del - defaults }
80
+ end
81
+
82
+ def self.js_extend_properties
83
+ {
84
+ :init_component => <<-END_OF_JAVASCRIPT.l,
85
+ function(){
86
+ #{js_full_class_name}.superclass.initComponent.call(this);
87
+
88
+ // Automatically set the correct editor for the default_value column
89
+ this.on('beforeedit', function(e){
90
+ var column = this.getColumnModel().getColumnById(this.getColumnModel().getColumnId(e.column));
91
+ var record = this.getStore().getAt(e.row);
92
+
93
+ if (column.dataIndex === "default_value") {
94
+ if (record.get("name") === this.pri) {
95
+ // Don't allow setting default value for the primary key
96
+ column.setEditor(null);
97
+ } else {
98
+ // Auto set the editor, dependent on the field type
99
+ var attrType = record.get("attr_type");
100
+ column.setEditor(Ext.create({xtype: this.attrTypeEditorMap[attrType] || "textfield"}));
101
+ }
102
+ }
103
+ }, this);
104
+
105
+ // Add push menu item to column context menus
106
+ this.on("viewready", this.extendColumnMenu, this);
107
+ }
108
+ END_OF_JAVASCRIPT
109
+
110
+ :extend_column_menu => <<-END_OF_JAVASCRIPT.l,
111
+ function(){
112
+ this.getView().hmenu.add("-", {text: "Propagate", handler: this.onPushToViews, scope: this});
113
+ }
114
+ END_OF_JAVASCRIPT
115
+
116
+ :on_push_to_views => <<-END_OF_JAVASCRIPT.l,
117
+ function(){
118
+ var columnIndex = this.getView().hdCtxIndex,
119
+ dataIndex = this.getColumnModel().getDataIndex(columnIndex);
120
+
121
+ this.pushDefaultsForAttr({name: dataIndex});
122
+ }
123
+ END_OF_JAVASCRIPT
124
+
125
+ :on_defaults => <<-END_OF_JAVASCRIPT.l,
126
+ function(){
127
+ Ext.Msg.confirm('Confirm', 'Are you sure?', function(btn){
128
+ if (btn == 'yes') {
129
+ this.loadDefaults();
130
+ }
131
+ }, this);
132
+ }
133
+ END_OF_JAVASCRIPT
134
+ }
135
+ end
136
+
137
+ api :push_defaults_for_attr
138
+ def push_defaults_for_attr(params)
139
+ NetzkeFieldList.update_children_on_attr(config[:model], params[:name])
140
+ {:feedback => "Done."}
141
+ end
142
+
143
+ def load_defaults(params)
144
+ data_class.replace_data(default_model_attrs)
145
+ on_data_changed
146
+ {:load_store_data => get_data}
147
+ end
148
+
149
+ private
150
+ # An override
151
+ def process_data(data, operation)
152
+ if operation == :update
153
+ meta_attrs_to_update = data.inject({}) do |r,el|
154
+ r.merge({
155
+ data_class.find(el["id"]).name => el.reject{ |k,v| k == "id" }
156
+ })
157
+ end
158
+
159
+ res = super
160
+ NetzkeModelAttrList.update_fields(config[:model], meta_attrs_to_update)
161
+ # NetzkeFieldList.update_children(config[:model], meta_attrs_to_update)
162
+ res
163
+ else
164
+ # NetzkeModelAttrList.add_attrs(config[:model], data.reject{ |k,v| k == "id" })
165
+ super
166
+ end
167
+ end
168
+
169
+ # An override
170
+ def store_data(data)
171
+ NetzkeModelAttrList.update_list_for_current_authority(config[:model], data)
172
+ # Let's try to do it through process_data
173
+ # NetzkeFieldList.write_attrs_for_model(config[:model], data)
174
+ end
175
+
176
+ # An override
177
+ def initial_data
178
+ # NetzkeModelAttrList.attrs_for_model(config[:model])
179
+ NetzkeModelAttrList.read_list(config[:model]) || default_model_attrs
180
+ # NetzkeModelAttrList.read_attrs_for_model(config[:model]) || default_model_attrs
181
+ end
182
+
183
+ # Default model attributes, along with their defaults meta-attributes (like :label)
184
+ def default_model_attrs
185
+ @default_model_attrs ||= begin
186
+ config[:model].constantize.netzke_attributes.map do |attr|
187
+ attr.merge(
188
+ :label => attr[:label] || attr[:name].humanize,
189
+ :attr_type => attr[:attr_type].to_s
190
+ )
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end