netzke-basepack 0.5.8 → 0.5.9

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