netzke-core 0.3.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,9 @@
1
1
  class NetzkeController < ActionController::Base
2
2
 
3
+ def index
4
+ redirect_to :action => :test_widgets
5
+ end
6
+
3
7
  # collect javascripts from all plugins that registered it in Netzke::Base.config[:javascripts]
4
8
  def netzke
5
9
  respond_to do |format|
@@ -23,15 +27,68 @@ class NetzkeController < ActionController::Base
23
27
  end
24
28
  end
25
29
 
26
- def method_missing(action)
27
- respond_to do |format|
28
- format.js {
29
- render :text => "#{action}.js"
30
- }
31
- format.css {
32
- render :text => "#{action}.css"
33
- }
30
+ #
31
+ # Primitive tests to quickly test the widgets
32
+ #
33
+
34
+ # FormPanel
35
+ netzke :form_panel, :persistent_config => false, :label_align => "top", :columns => [
36
+ {:name => 'field_one', :xtype => 'textarea'},
37
+ {:name => 'field_two', :xtype => 'textarea'}
38
+ ]
39
+
40
+ # BorderLayoutPanel
41
+ netzke :border_layout_panel, :regions => {
42
+ :west => {
43
+ :widget_class_name => "Panel",
44
+ :region_config => {:width => 300, :split => true}
45
+ },
46
+ :center => {
47
+ :widget_class_name => "Panel"
48
+ }
49
+ }
50
+
51
+ # TabPanel
52
+ netzke :tab_panel, :items => [{
53
+ :widget_class_name => "Panel",
54
+ :ext_config => {
55
+ :html => "Panel 1",
56
+ },
57
+ # :active => true
58
+ },{
59
+ :widget_class_name => "Panel",
60
+ :ext_config => {
61
+ :html => "Panel 2",
62
+ }
63
+ }]
64
+
65
+ # AccordionPanel
66
+ netzke :accordion_panel, :items => [{
67
+ :widget_class_name => "Panel",
68
+ :ext_config => {
69
+ :html => "Panel 1",
70
+ },
71
+ :active => true
72
+ },{
73
+ :widget_class_name => "Panel",
74
+ :ext_config => {
75
+ :html => "Panel 2",
76
+ }
77
+ }]
78
+
79
+ # BasicApp
80
+ netzke :basic_app
81
+
82
+ netzke :panel, :ext_config => {:html => "Panel", :title => "Test panel", :border => true}
83
+
84
+ def test_widgets
85
+ html = "<h3>Quick primitive widgets tests</h3>"
86
+
87
+ self.class.widget_config_storage.each_key.map(&:to_s).sort.each do |w|
88
+ html << "<a href='#{w}_test'>#{w.to_s.humanize}</a><br/>\n"
34
89
  end
90
+
91
+ render :text => html
35
92
  end
36
-
93
+
37
94
  end
@@ -11,10 +11,6 @@ class NetzkePreference < ActiveRecord::Base
11
11
 
12
12
  ELEMENTARY_CONVERTION_METHODS= {'Fixnum' => 'to_i', 'String' => 'to_s', 'Float' => 'to_f', 'Symbol' => 'to_sym'}
13
13
 
14
- # def self.user_id
15
- # Netzke::Base.user && Netzke::Base.user.id
16
- # end
17
-
18
14
  def self.widget_name=(value)
19
15
  @@widget_name = value
20
16
  end
@@ -67,18 +63,6 @@ class NetzkePreference < ActiveRecord::Base
67
63
  end
68
64
  end
69
65
 
70
- # execute set/get operation for a specified widget, e.g.:
71
- # NetzkePreference.for_widget('my_widget') { |p| p[:key] = "value" }
72
- def self.for_widget(widget, &block)
73
- raise ArgumentError, "Block is required for #{self.name}\#for_widget" if !block_given?
74
- backup_widget_name = self.widget_name
75
- self.widget_name = widget
76
- res = yield(self)
77
- self.widget_name = backup_widget_name
78
- res
79
- end
80
-
81
- #
82
66
  # Overwrite pref_to_read, pref_to_write methods, and find_all_for_widget if you want a different way of
83
67
  # identifying the proper preference based on your own authorization strategy.
84
68
  #
@@ -97,14 +81,25 @@ class NetzkePreference < ActiveRecord::Base
97
81
 
98
82
  if session[:masq_user]
99
83
  # first, get the prefs for this user it they exist
100
- res = self.find(:first, :conditions => cond.merge({:user_id => session[:masq_user].id}))
84
+ res = self.find(:first, :conditions => cond.merge({:user_id => session[:masq_user]}))
101
85
  # if it doesn't exist, get them for the user's role
102
- res ||= self.find(:first, :conditions => cond.merge({:role_id => session[:masq_user].role.id}))
86
+ user = User.find(session[:masq_user])
87
+ res ||= self.find(:first, :conditions => cond.merge({:role_id => user.role.id}))
88
+ # if it doesn't exist either, get them for the World (role_id = 0)
89
+ res ||= self.find(:first, :conditions => cond.merge({:role_id => 0}))
103
90
  elsif session[:masq_role]
104
- res = self.find(:first, :conditions => cond.merge({:role_id => session[:masq_role].id}))
105
- elsif session[:user]
106
- res = self.find(:first, :conditions => cond.merge({:user_id => session[:user].id}))
107
- res ||= self.find(:first, :conditions => cond.merge({:role_id => session[:user].role.try(:id)}))
91
+ # first, get the prefs for this role
92
+ res = self.find(:first, :conditions => cond.merge({:role_id => session[:masq_role]}))
93
+ # if it doesn't exist, get them for the World (role_id = 0)
94
+ res ||= self.find(:first, :conditions => cond.merge({:role_id => 0}))
95
+ elsif session[:netzke_user_id]
96
+ user = User.find(session[:netzke_user_id])
97
+ # first, get the prefs for this user
98
+ res = self.find(:first, :conditions => cond.merge({:user_id => user.id}))
99
+ # if it doesn't exist, get them for the user's role
100
+ res ||= self.find(:first, :conditions => cond.merge({:role_id => user.role.id}))
101
+ # if it doesn't exist either, get them for the World (role_id = 0)
102
+ res ||= self.find(:first, :conditions => cond.merge({:role_id => 0}))
108
103
  else
109
104
  res = self.find(:first, :conditions => cond)
110
105
  end
@@ -118,20 +113,27 @@ class NetzkePreference < ActiveRecord::Base
118
113
  cond = {:name => name, :widget_name => self.widget_name}
119
114
 
120
115
  if session[:masq_user]
121
- cond.merge!({:user_id => session[:masq_user].id})
116
+ cond.merge!({:user_id => session[:masq_user]})
117
+ # first, try to find the preference for masq_user
122
118
  res = self.find(:first, :conditions => cond)
119
+ # if it doesn't exist, create it
123
120
  res ||= self.new(cond)
124
121
  elsif session[:masq_role]
125
122
  # first, delete all the corresponding preferences for the users that have this role
126
- Role.find(session[:masq_role].id).users.each do |u|
123
+ Role.find(session[:masq_role]).users.each do |u|
127
124
  self.delete_all(cond.merge({:user_id => u.id}))
128
125
  end
129
- cond.merge!({:role_id => session[:masq_role].id})
126
+ cond.merge!({:role_id => session[:masq_role]})
130
127
  res = self.find(:first, :conditions => cond)
131
128
  res ||= self.new(cond)
132
- elsif session[:user]
133
- res = self.find(:first, :conditions => cond.merge({:user_id => session[:user].id}))
134
- res ||= self.new(cond.merge({:user_id => session[:user].id}))
129
+ elsif session[:masq_world]
130
+ # first, delete all the corresponding preferences for all users and roles
131
+ self.delete_all(cond)
132
+ # then, create the new preference for the World (role_id = 0)
133
+ res = self.new(cond.merge(:role_id => 0))
134
+ elsif session[:netzke_user_id]
135
+ res = self.find(:first, :conditions => cond.merge({:user_id => session[:netzke_user_id]}))
136
+ res ||= self.new(cond.merge({:user_id => session[:netzke_user_id]}))
135
137
  else
136
138
  res = self.find(:first, :conditions => cond)
137
139
  res ||= self.new(cond)
@@ -144,11 +146,12 @@ class NetzkePreference < ActiveRecord::Base
144
146
  cond = {:widget_name => name}
145
147
 
146
148
  if session[:masq_user] || session[:masq_role]
147
- cond.merge!({:user_id => session[:masq_user].try(:id), :role_id => session[:masq_role].try(:id)})
149
+ cond.merge!({:user_id => session[:masq_user], :role_id => session[:masq_role]})
148
150
  res = self.find(:all, :conditions => cond)
149
- elsif session[:user]
150
- res = self.find(:all, :conditions => cond.merge({:user_id => session[:user].id}))
151
- res += self.find(:all, :conditions => cond.merge({:role_id => session[:user].role.try(:id)}))
151
+ elsif session[:netzke_user_id]
152
+ user = User.find(session[:netzke_user_id])
153
+ res = self.find(:all, :conditions => cond.merge({:user_id => session[:netzke_user_id]}))
154
+ res += self.find(:all, :conditions => cond.merge({:role_id => user.role.try(:id)}))
152
155
  else
153
156
  res = self.find(:all, :conditions => cond)
154
157
  end
@@ -156,6 +159,10 @@ class NetzkePreference < ActiveRecord::Base
156
159
  res
157
160
  end
158
161
 
162
+ def self.delete_all_for_widget(name)
163
+ self.destroy(find_all_for_widget(name))
164
+ end
165
+
159
166
  private
160
167
  def self.normalize_preference_name(name)
161
168
  name.to_s.gsub(".", "__").gsub("/", "__")
data/lib/netzke-core.rb CHANGED
@@ -10,9 +10,6 @@ require 'netzke/routing'
10
10
 
11
11
  require 'netzke/feedback_ghost'
12
12
 
13
- # Vendor
14
- require 'vendor/facets/hash/recursive_merge'
15
-
16
13
  # Load models and controllers from lib/app
17
14
  %w{ models controllers }.each do |dir|
18
15
  path = File.join(File.dirname(__FILE__), 'app', dir)
data/lib/netzke/base.rb CHANGED
@@ -1,26 +1,73 @@
1
- require 'netzke/base_extras/js_builder'
2
- require 'netzke/base_extras/interface'
1
+ require 'netzke/base_js'
3
2
 
4
3
  module Netzke
4
+ # = Base
5
+ # Base class for every Netzke widget
6
+ #
7
+ # To instantiate a widget in the controller:
8
+ #
9
+ # netzke :widget_name, configuration_hash
10
+ #
11
+ # == Configuration
12
+ # * <tt>:widget_class_name</tt> - name of the widget class in the scope of the Netzke module, e.g. "FormPanel".
13
+ # When a widget is defined in the controller and this option is omitted, widget class is deferred from the widget's
14
+ # name. E.g.:
15
+ #
16
+ # netzke :grid_panel, :data_class_name => "User"
17
+ #
18
+ # In this case <tt>:widget_class_name</tt> is assumed to be "GridPanel"
19
+ #
20
+ # * <tt>:ext_config</tt> - a config hash that is used to create a javascript instance of the widget. Every
21
+ # configuration that comes here will be available inside the javascript instance of the widget.
22
+ # * <tt>:persistent_config</tt> - if set to <tt>true</tt>, the widget will use persistent storage to store its state;
23
+ # for instance, Netzke::GridPanel stores there its columns state (width, visibility, order, headers, etc).
24
+ # A widget may or may not provide interface to its persistent settings. GridPanel and FormPanel from netzke-basepack
25
+ # are examples of widgets that by default do.
26
+ #
27
+ # Examples of configuration:
28
+ #
29
+ # netzke :books,
30
+ # :widget_class_name => "GridPanel",
31
+ # :data_class_name => "Book", # GridPanel specific option
32
+ # :ext_config => {
33
+ # :icon_cls => 'icon-grid',
34
+ # :title => "My books"
35
+ # }
36
+ #
37
+ # netzke :form_panel,
38
+ # :data_class_name => "User" # FormPanel specific option
5
39
  class Base
6
-
7
- # Class-level Netzke::Base configuration. The defaults also get specified here.
8
- def self.config
9
- set_default_config({
10
- # which javascripts and stylesheets must get included at the initial load (see netzke-core.rb)
11
- :javascripts => [],
12
- :stylesheets => [],
13
-
14
- :persistent_config_manager => "NetzkePreference",
15
-
16
- :ext_location => defined?(RAILS_ROOT) && "#{RAILS_ROOT}/public/extjs"
17
- })
18
- end
19
-
20
- include Netzke::BaseExtras::JsBuilder
21
- include Netzke::BaseExtras::Interface
22
-
40
+ include Netzke::BaseJs # javascript (client-side)
41
+
23
42
  module ClassMethods
43
+ # Class-level Netzke::Base configuration. The defaults also get specified here.
44
+ def config
45
+ set_default_config({
46
+ # which javascripts and stylesheets must get included at the initial load (see netzke-core.rb)
47
+ :javascripts => [],
48
+ :stylesheets => [],
49
+
50
+ :persistent_config_manager => "NetzkePreference",
51
+ :ext_location => defined?(RAILS_ROOT) && "#{RAILS_ROOT}/public/extjs",
52
+ :default_config => {
53
+ :persistent_config => true,
54
+ :ext_config => {}
55
+ }
56
+ })
57
+ end
58
+
59
+ def configure(*args)
60
+ if args.first.is_a?(Symbol)
61
+ # first arg is a Symbol
62
+ config[args.first] = args.last
63
+ else
64
+ config.deep_merge!(args.first)
65
+ end
66
+
67
+ enforce_config_consistency
68
+ end
69
+
70
+ def enforce_config_consistency; end
24
71
 
25
72
  # "Netzke::SomeWidget" => "SomeWidget"
26
73
  def short_widget_class_name
@@ -55,30 +102,31 @@ module Netzke
55
102
  session[:_netzke_next_request_is_first_after_logout] = true
56
103
  end
57
104
 
58
- #
59
- # Use this class method to declare connection points between client side of a widget and its server side. A method in a widget class with the same name will be (magically) called by the client side of the widget. See Grid widget for an example
60
- #
61
- def interface(*interface_points)
62
- interfacep = read_inheritable_attribute(:interface_points) || []
63
- interface_points.each{|p| interfacep << p}
64
- write_inheritable_attribute(:interface_points, interfacep)
105
+ # Use this class method to declare connection points between client side of a widget and its server side.
106
+ # A method in a widget class with the same name will be (magically) called by the client side of the widget.
107
+ # See netzke-basepack's GridPanel for an example.
108
+ def api(*api_points)
109
+ apip = read_inheritable_attribute(:api_points) || []
110
+ api_points.each{|p| apip << p}
111
+ write_inheritable_attribute(:api_points, apip)
65
112
 
66
- interface_points.each do |interfacep|
113
+ # It may be needed later for security
114
+ api_points.each do |apip|
67
115
  module_eval <<-END, __FILE__, __LINE__
68
- def interface_#{interfacep}(*args)
69
- #{interfacep}(*args).to_js
116
+ def api_#{apip}(*args)
117
+ #{apip}(*args).to_nifty_json
70
118
  end
71
119
  # FIXME: commented out because otherwise ColumnOperations stop working
72
- # def #{interfacep}(*args)
73
- # flash :warning => "API point '#{interfacep}' is not implemented for widget '#{short_widget_class_name}'"
120
+ # def #{apip}(*args)
121
+ # flash :warning => "API point '#{apip}' is not implemented for widget '#{short_widget_class_name}'"
74
122
  # {:flash => @flash}
75
123
  # end
76
124
  END
77
125
  end
78
126
  end
79
127
 
80
- def interface_points
81
- read_inheritable_attribute(:interface_points)
128
+ def api_points
129
+ read_inheritable_attribute(:api_points)
82
130
  end
83
131
 
84
132
  # returns an instance of a widget defined in the config
@@ -98,78 +146,176 @@ module Netzke
98
146
  def persistent_config
99
147
  # if the class is not present, fake it (it will not store anything, and always return nil)
100
148
  if persistent_config_manager_class.nil?
101
- fake_config = {}
102
- class << fake_config
103
- def for_widget(*params, &block)
104
- yield({})
105
- end
106
- def widget_name=(*params)
107
- end
108
- end
109
- fake_config
149
+ {}
110
150
  else
111
151
  persistent_config_manager_class
112
152
  end
113
153
  end
114
154
 
115
155
  private
116
- def set_default_config(default_config)
156
+ def set_default_config(c)
117
157
  @@config ||= {}
118
- @@config[self.name] ||= default_config
119
- @@config[self.name]
158
+ @@config[self.name] ||= c
120
159
  end
121
160
 
122
161
  end
123
162
  extend ClassMethods
124
163
 
125
- attr_accessor :config, :server_confg, :parent, :logger, :id_name, :permissions, :session
126
- attr_reader :pref
164
+ attr_accessor :parent, :name, :id_name, :permissions, :session
165
+
166
+ api :load_aggregatee_with_cache # every widget gets this api
127
167
 
168
+ # Widget initialization process
169
+ # * the config hash is available to the widget after the "super" call in the initializer
170
+ # * override/add new default configuration options into the "default_config" method
171
+ # (the config hash is not yet available)
128
172
  def initialize(config = {}, parent = nil)
129
- @session = Netzke::Base.session
173
+ @session = Netzke::Base.session
174
+ @passed_config = config # configuration passed at the moment of instantiation
175
+ @parent = parent
176
+ @name = config[:name].nil? ? short_widget_class_name.underscore : config[:name].to_s
177
+ @id_name = parent.nil? ? @name : "#{parent.id_name}__#{@name}"
178
+ @flash = []
179
+ end
180
+
181
+ # add flatten method to Hash
182
+ Hash.class_eval do
183
+ def flatten(preffix = "")
184
+ res = []
185
+ self.each_pair do |k,v|
186
+ if v.is_a?(Hash)
187
+ res += v.flatten(k)
188
+ else
189
+ res << {
190
+ :name => ((preffix.to_s.empty? ? "" : preffix.to_s + "__") + k.to_s).to_sym,
191
+ :value => v,
192
+ :type => (["TrueClass", "FalseClass"].include?(v.class.name) ? 'Boolean' : v.class.name).to_sym
193
+ }
194
+ end
195
+ end
196
+ res
197
+ end
198
+ end
130
199
 
131
- # Uncomment for application-wide weak/strong default config for widgets
132
- # @config = (session[:weak_default_config] || {}).
133
- # recursive_merge(initial_config).
134
- # recursive_merge(config).
135
- # recursive_merge(session[:strong_default_config] || {})
200
+ def default_config
201
+ self.class.config[:default_config].nil? ? {} : {}.merge!(self.class.config[:default_config])
202
+ end
136
203
 
137
- @config = initial_config.recursive_merge(config)
138
-
139
- @parent = parent
140
-
141
- @id_name = parent.nil? ? config[:name].to_s : "#{parent.id_name}__#{config[:name]}"
142
-
143
- @flash = []
144
-
145
- @config[:ext_config] ||= {} # configuration used to instantiate JS class
204
+ # Access to the config that takes into account all possible ways to configure a widget. *Read only*.
205
+ def config
206
+ # Translates into something like this:
207
+ # @config ||= default_config.
208
+ # deep_merge(@passed_config).
209
+ # deep_merge(persistent_config_hash).
210
+ # deep_merge(strong_parent_config).
211
+ # deep_merge(strong_session_config)
212
+ @config ||= independent_config.
213
+ deep_merge(strong_parent_config).
214
+ deep_merge(strong_session_config)
215
+
216
+ end
217
+
218
+ def flat_config(key = nil)
219
+ fc = config.flatten
220
+ key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
221
+ end
146
222
 
147
- process_permissions_config
223
+ def strong_parent_config
224
+ @strong_parent_config ||= parent.nil? ? {} : parent.strong_children_config
148
225
  end
149
226
 
150
- # Rails' logger
151
- def logger
152
- Rails.logger
227
+ # Config that is not overwritten by parents and sessions
228
+ def independent_config
229
+ @independent_config ||= initial_config.deep_merge(persistent_config_hash)
153
230
  end
154
231
 
155
- # configuration of all children will get recursive_merge'd with strong_children_config
156
- def strong_children_config= (c)
157
- @strong_children_config = c
232
+ def flat_independent_config(key = nil)
233
+ fc = independent_config.flatten
234
+ key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
158
235
  end
159
236
 
160
- def strong_children_config
161
- @strong_children_config ||= {}
237
+ def flat_default_config(key = nil)
238
+ fc = default_config.flatten
239
+ key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
240
+ end
241
+
242
+ # Static, hardcoded config. Consists of default values merged with config that was passed during instantiation
243
+ def initial_config
244
+ @initial_config ||= default_config.deep_merge(@passed_config)
245
+ end
246
+
247
+ def flat_initial_config(key = nil)
248
+ fc = initial_config.flatten
249
+ key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
250
+ end
251
+
252
+ def build_persistent_config_hash
253
+ return {} if !initial_config[:persistent_config]
254
+
255
+ prefs = NetzkePreference.find_all_for_widget(id_name)
256
+ res = {}
257
+ prefs.each do |p|
258
+ hsh_levels = p.name.split("__").map(&:to_sym)
259
+ tmp_res = {} # it decends into itself, building itself
260
+ anchor = {} # it will keep the tail of tmp_res
261
+ hsh_levels.each do |level_prefix|
262
+ tmp_res[level_prefix] ||= level_prefix == hsh_levels.last ? p.normalized_value : {}
263
+ anchor = tmp_res[level_prefix] if level_prefix == hsh_levels.first
264
+ tmp_res = tmp_res[level_prefix]
265
+ end
266
+ # Now 'anchor' is a hash that represents the path to the single value,
267
+ # for example: {:ext_config => {:title => 100}} (which corresponds to ext_config__title)
268
+ # So we need to recursively merge it into the final result
269
+ res.deep_merge!(hsh_levels.first => anchor)
270
+ end
271
+ res
272
+ end
273
+
274
+ def persistent_config_hash
275
+ @persistent_config_hash ||= build_persistent_config_hash
276
+ end
277
+
278
+ def ext_config
279
+ config[:ext_config] || {}
162
280
  end
163
281
 
164
- # configuration of all children will get reverse_recursive_merge'd with weak_children_config
165
- def weak_children_config= (c)
166
- @weak_children_config = c
282
+ # Like normal config, but stored in session
283
+ def weak_session_config
284
+ widget_session[:weak_session_config] ||= {}
285
+ end
286
+
287
+ def strong_session_config
288
+ widget_session[:strong_session_config] ||= {}
167
289
  end
290
+
291
+ # configuration of all children will get deep_merge'd with strong_children_config
292
+ # def strong_children_config= (c)
293
+ # @strong_children_config = c
294
+ # end
295
+
296
+ # This config will be picked up by all the descendants
297
+ def strong_children_config
298
+ @strong_children_config ||= parent.nil? ? {} : parent.strong_children_config
299
+ end
300
+
301
+ # configuration of all children will get reverse_deep_merge'd with weak_children_config
302
+ # def weak_children_config= (c)
303
+ # @weak_children_config = c
304
+ # end
168
305
 
169
306
  def weak_children_config
170
307
  @weak_children_config ||= {}
171
308
  end
172
309
 
310
+ def widget_session
311
+ session[id_name] ||= {}
312
+ end
313
+
314
+ # Rails' logger
315
+ def logger
316
+ Rails.logger
317
+ end
318
+
173
319
  def dependency_classes
174
320
  res = []
175
321
  non_late_aggregatees.keys.each do |aggr|
@@ -190,21 +336,16 @@ module Netzke
190
336
  config_class
191
337
  else
192
338
  # if we can't use presistent config, all the calls to it will always return nil, and the "="-operation will be ignored
339
+ logger.debug "==> NETZKE: no persistent config is set up for widget '#{id_name}'"
193
340
  {}
194
341
  end
195
342
  end
196
343
 
197
- def initial_config
198
- {}
199
- end
200
-
201
344
  # 'Netzke::Grid' => 'Grid'
202
345
  def short_widget_class_name
203
346
  self.class.short_widget_class_name
204
347
  end
205
348
 
206
- interface :get_widget # every widget gets this
207
-
208
349
  ## Dependencies
209
350
  def dependencies
210
351
  @dependencies ||= begin
@@ -235,6 +376,13 @@ module Netzke
235
376
  aggregatees.merge!(aggr)
236
377
  end
237
378
 
379
+ def remove_aggregatee(aggr)
380
+ if config[:persistent_config]
381
+ persistent_config_manager_class.delete_all_for_widget("#{id_name}__#{aggr}")
382
+ end
383
+ aggregatees[aggr] = nil
384
+ end
385
+
238
386
  # The difference between aggregatees and late aggregatees is the following: the former gets instantiated together with its aggregator and is normally *instantly* visible as a part of it (for example, the widget in the initially expanded panel in an Accordion). A late aggregatee doesn't get instantiated along with its aggregator. Until it gets requested from the server, it doesn't take any part in its aggregator's life. An example of late aggregatee could be a widget that is loaded dynamically into a previously collapsed panel of an Accordion, or a preferences window (late aggregatee) for a widget (aggregator) that only gets shown when user wants to edit widget's preferences.
239
387
  def initial_late_aggregatees
240
388
  {}
@@ -245,22 +393,24 @@ module Netzke
245
393
  end
246
394
 
247
395
  # recursively instantiates an aggregatee based on its "path": e.g. if we have an aggregatee :aggr1 which in its turn has an aggregatee :aggr10, the path to the latter would be "aggr1__aggr10"
248
- def aggregatee_instance(name)
396
+ def aggregatee_instance(name, strong_config = {})
249
397
  aggregator = self
250
398
  name.to_s.split('__').each do |aggr|
251
399
  aggr = aggr.to_sym
252
- short_class_name = aggregator.aggregatees[aggr][:widget_class_name]
253
- raise ArgumentError, "No widget_class_name specified for aggregatee #{aggr} of #{aggregator.config[:name]}" if short_class_name.nil?
400
+ aggregatee_config = aggregator.aggregatees[aggr]
401
+ raise ArgumentError, "No aggregatee '#{aggr}' defined for widget '#{aggregator.id_name}'" if aggregatee_config.nil?
402
+ short_class_name = aggregatee_config[:widget_class_name]
403
+ raise ArgumentError, "No widget_class_name specified for aggregatee #{aggr} of #{aggregator.id_name}" if short_class_name.nil?
254
404
  widget_class = "Netzke::#{short_class_name}".constantize
255
405
 
256
406
  conf = weak_children_config.
257
- recursive_merge(aggregator.aggregatees[aggr]).
258
- recursive_merge(strong_children_config).
407
+ deep_merge(aggregatee_config).
408
+ deep_merge(strong_config). # we may want to reconfigure the aggregatee at the moment of instantiation
259
409
  merge(:name => aggr)
260
410
 
261
411
  aggregator = widget_class.new(conf, aggregator) # params: config, parent
262
- aggregator.weak_children_config = weak_children_config
263
- aggregator.strong_children_config = strong_children_config
412
+ # aggregator.weak_children_config = weak_children_config
413
+ # aggregator.strong_children_config = strong_children_config
264
414
  end
265
415
  aggregator
266
416
  end
@@ -280,35 +430,90 @@ module Netzke
280
430
  end
281
431
 
282
432
  # permissions
283
- def available_permissions
284
- []
285
- end
433
+ # def available_permissions
434
+ # []
435
+ # end
436
+
437
+ # def process_permissions_config
438
+ # if !available_permissions.empty?
439
+ # # First, process permissions from the config
440
+ # @permissions = available_permissions.inject({}){|h,p| h.merge(p.to_sym => true)} # by default anything is allowed
441
+ #
442
+ # config[:prohibit] = available_permissions if config[:prohibit] == :all # short-cut for all permissions
443
+ # config[:prohibit] = [config[:prohibit]] if config[:prohibit].is_a?(Symbol) # so that config[:prohibit] => :write works
444
+ # config[:prohibit] && config[:prohibit].each{|p| @permissions.merge!(p.to_sym => false)} # prohibit
445
+ #
446
+ # config[:allow] = [config[:allow]] if config[:allow].is_a?(Symbol) # so that config[:allow] => :write works
447
+ # config[:allow] && config[:allow].each{|p| @permissions.merge!(p.to_sym => true)} # allow
448
+ #
449
+ # # ... and then merge it with NetzkePreferences
450
+ # available_permissions.each do |p|
451
+ # # if nothing is stored in persistent_config, store the permission from the config; otherwise leave what's there
452
+ # persistent_config["permissions/#{p}"].nil? && persistent_config["permissions/#{p}"] = @permissions[p.to_sym]
453
+ #
454
+ # # what's stored in persistent_config has higher priority, so, if there's something there, use that
455
+ # persistent_permisson = persistent_config["permissions/#{p}"]
456
+ # @permissions[p.to_sym] = persistent_permisson unless persistent_permisson.nil?
457
+ # end
458
+ # end
459
+ # end
286
460
 
287
- def process_permissions_config
288
- if !available_permissions.empty?
289
- # First, process permissions from the config
290
- @permissions = available_permissions.inject({}){|h,p| h.merge(p.to_sym => true)} # by default anything is allowed
461
+ # called when the method_missing tries to processes a non-existing aggregatee
462
+ def aggregatee_missing(aggr)
463
+ flash :error => "Unknown aggregatee #{aggr} for widget #{name}"
464
+ {:feedback => @flash}.to_nifty_json
465
+ end
291
466
 
292
- config[:prohibit] = available_permissions if config[:prohibit] == :all # short-cut for all permissions
293
- config[:prohibit] = [config[:prohibit]] if config[:prohibit].is_a?(Symbol) # so that config[:prohibit] => :write works
294
- config[:prohibit] && config[:prohibit].each{|p| @permissions.merge!(p.to_sym => false)} # prohibit
467
+ def tools
468
+ persistent_config[:tools] ||= config[:tools] || []
469
+ end
295
470
 
296
- config[:allow] = [config[:allow]] if config[:allow].is_a?(Symbol) # so that config[:allow] => :write works
297
- config[:allow] && config[:allow].each{|p| @permissions.merge!(p.to_sym => true)} # allow
298
-
299
- # ... and then merge it with NetzkePreferences
300
- available_permissions.each do |p|
301
- # if nothing is stored in persistent_config, store the permission from the config; otherwise leave what's there
302
- persistent_config["permissions/#{p}"].nil? && persistent_config["permissions/#{p}"] = @permissions[p.to_sym]
471
+ def menu
472
+ persistent_config[:menu] ||= config[:menu] == false ? nil : config[:menu]
473
+ end
474
+
475
+ # some convenience for instances
476
+ def persistent_config_manager_class
477
+ self.class.persistent_config_manager_class
478
+ end
303
479
 
304
- # what's stored in persistent_config has higher priority, so, if there's something there, use that
305
- persistent_permisson = persistent_config["permissions/#{p}"]
306
- @permissions[p.to_sym] = persistent_permisson unless persistent_permisson.nil?
307
- end
308
- end
480
+ # override this method to do stuff at the moment of loading by some parent
481
+ def before_load
482
+ widget_session.clear
309
483
  end
310
484
 
311
- # method dispatcher - sends method to the proper aggregatee
485
+ # API
486
+ def load_aggregatee_with_cache(params)
487
+ cache = ActiveSupport::JSON.decode(params.delete(:cache))
488
+ relative_widget_id = params.delete(:id).underscore
489
+ passed_config = params[:config] && ActiveSupport::JSON.decode(params[:config]) || {}
490
+ passed_config = passed_config.symbolize_keys
491
+ widget = aggregatee_instance(relative_widget_id, passed_config)
492
+
493
+ # inform the widget that it's being loaded
494
+ widget.before_load
495
+
496
+ [{
497
+ :js => widget.js_missing_code(cache),
498
+ :css => widget.css_missing_code(cache)
499
+ }, {
500
+ :render_widget_in_container => {
501
+ :container => params[:container],
502
+ :config => widget.js_config
503
+ }
504
+ }, {
505
+ :widget_loaded => {
506
+ :id => relative_widget_id
507
+ }
508
+ }]
509
+ end
510
+
511
+ # Method dispatcher - instantiates an aggregatee and calls the method on it
512
+ # E.g.:
513
+ # users__center__get_data
514
+ # instantiates aggregatee "users", and calls "center__get_data" on it
515
+ # books__move_column
516
+ # instantiates aggregatee "books", and calls "api_move_column" on it
312
517
  def method_missing(method_name, params = {})
313
518
  widget, *action = method_name.to_s.split('__')
314
519
  widget = widget.to_sym
@@ -316,9 +521,9 @@ module Netzke
316
521
 
317
522
  if action
318
523
  if aggregatees[widget]
319
- # only actions starting with "interface_" are accessible
320
- interface_action = action.to_s.index('__') ? action : "interface_#{action}"
321
- aggregatee_instance(widget).send(interface_action, params)
524
+ # only actions starting with "api_" are accessible
525
+ api_action = action.to_s.index('__') ? action : "api_#{action}"
526
+ aggregatee_instance(widget).send(api_action, params)
322
527
  else
323
528
  aggregatee_missing(widget)
324
529
  end
@@ -326,33 +531,6 @@ module Netzke
326
531
  super
327
532
  end
328
533
  end
329
-
330
- # called when the method_missing tries to processes a non-existing aggregatee
331
- def aggregatee_missing(aggr)
332
- flash :error => "Unknown aggregatee #{aggr} for widget #{config[:name]}"
333
- {:success => false, :flash => @flash}.to_json
334
- end
335
-
336
- def tools
337
- persistent_config[:tools] ||= config[:tools] == false ? nil : config[:tools]
338
- end
339
-
340
- def bbar
341
- persistent_config[:bottom_bar] ||= config[:bbar] == false ? nil : config[:bbar]
342
- end
343
-
344
- def tbar
345
- persistent_config[:top_bar] ||= config[:tbar] == false ? nil : config[:tbar]
346
- end
347
-
348
- def menu
349
- persistent_config[:menu] ||= config[:menu] == false ? nil : config[:menu]
350
- end
351
534
 
352
- # some convenience for instances
353
- def persistent_config_manager_class
354
- self.class.persistent_config_manager_class
355
- end
356
-
357
535
  end
358
536
  end