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.
- data/.autotest +1 -0
- data/.gitignore +4 -0
- data/CHANGELOG +21 -0
- data/Manifest +9 -11
- data/README.rdoc +12 -0
- data/Rakefile +17 -13
- data/TODO +2 -1
- data/VERSION +1 -0
- data/generators/netzke_core/templates/create_netzke_preferences.rb +1 -1
- data/javascripts/core.js +271 -113
- data/lib/app/controllers/netzke_controller.rb +66 -9
- data/lib/app/models/netzke_preference.rb +39 -32
- data/lib/netzke-core.rb +0 -3
- data/lib/netzke/base.rb +318 -140
- data/lib/netzke/base_js.rb +249 -0
- data/lib/netzke/controller_extensions.rb +29 -40
- data/lib/netzke/core_ext.rb +40 -18
- data/lib/netzke/feedback_ghost.rb +2 -2
- data/netzke-core.gemspec +90 -11
- data/test/unit/core_ext_test.rb +28 -7
- data/test/unit/netzke_core_test.rb +57 -29
- data/test/unit/netzke_preference_test.rb +7 -7
- metadata +35 -38
- data/README.mdown +0 -15
- data/lib/netzke/base_extras/interface.rb +0 -20
- data/lib/netzke/base_extras/js_builder.rb +0 -271
- data/lib/vendor/facets/hash/recursive_merge.rb +0 -28
@@ -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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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]
|
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
|
-
|
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
|
-
|
105
|
-
|
106
|
-
|
107
|
-
res ||= self.find(:first, :conditions => cond.merge({:role_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]
|
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]
|
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]
|
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[:
|
133
|
-
|
134
|
-
|
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]
|
149
|
+
cond.merge!({:user_id => session[:masq_user], :role_id => session[:masq_role]})
|
148
150
|
res = self.find(:all, :conditions => cond)
|
149
|
-
elsif session[:
|
150
|
-
|
151
|
-
res
|
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/
|
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
|
-
|
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
|
-
#
|
60
|
-
#
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
write_inheritable_attribute(:
|
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
|
-
|
113
|
+
# It may be needed later for security
|
114
|
+
api_points.each do |apip|
|
67
115
|
module_eval <<-END, __FILE__, __LINE__
|
68
|
-
def
|
69
|
-
#{
|
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 #{
|
73
|
-
# flash :warning => "API point '#{
|
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
|
81
|
-
read_inheritable_attribute(:
|
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
|
-
|
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(
|
156
|
+
def set_default_config(c)
|
117
157
|
@@config ||= {}
|
118
|
-
@@config[self.name] ||=
|
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 :
|
126
|
-
|
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
|
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
|
-
|
132
|
-
|
133
|
-
|
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
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
@
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
@config
|
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
|
-
|
223
|
+
def strong_parent_config
|
224
|
+
@strong_parent_config ||= parent.nil? ? {} : parent.strong_children_config
|
148
225
|
end
|
149
226
|
|
150
|
-
#
|
151
|
-
def
|
152
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
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
|
161
|
-
|
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
|
-
#
|
165
|
-
def
|
166
|
-
|
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
|
-
|
253
|
-
raise ArgumentError, "No
|
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
|
-
|
258
|
-
|
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
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
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
|
-
|
293
|
-
|
294
|
-
|
467
|
+
def tools
|
468
|
+
persistent_config[:tools] ||= config[:tools] || []
|
469
|
+
end
|
295
470
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
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
|
-
|
305
|
-
|
306
|
-
|
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
|
-
#
|
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 "
|
320
|
-
|
321
|
-
aggregatee_instance(widget).send(
|
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
|