skozlov-netzke-basepack 0.1.1.2 → 0.5.0

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 (81) hide show
  1. data/.autotest +1 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +2 -19
  4. data/README.rdoc +87 -0
  5. data/Rakefile +28 -12
  6. data/TODO.rdoc +7 -0
  7. data/VERSION +1 -0
  8. data/autotest/discover.rb +3 -0
  9. data/init.rb +0 -1
  10. data/javascripts/basepack.js +839 -49
  11. data/lib/app/models/netzke_auto_column.rb +56 -0
  12. data/lib/netzke/accordion_panel.rb +113 -0
  13. data/lib/netzke/active_record/basepack.rb +104 -0
  14. data/lib/netzke/active_record/data_accessor.rb +21 -0
  15. data/lib/netzke/basic_app.rb +325 -0
  16. data/lib/netzke/border_layout_panel.rb +128 -0
  17. data/lib/netzke/configuration_panel.rb +24 -0
  18. data/lib/netzke/data_accessor.rb +71 -0
  19. data/lib/netzke/ext.rb +6 -0
  20. data/lib/netzke/field_model.rb +131 -0
  21. data/lib/netzke/fields_configurator.rb +95 -0
  22. data/lib/netzke/form_panel.rb +214 -0
  23. data/lib/netzke/form_panel_api.rb +74 -0
  24. data/lib/netzke/form_panel_extras/javascripts/xcheckbox.js +82 -0
  25. data/lib/netzke/form_panel_js.rb +129 -0
  26. data/lib/netzke/grid_panel.rb +442 -0
  27. data/lib/netzke/grid_panel_api.rb +352 -0
  28. data/lib/netzke/grid_panel_extras/javascripts/check-column.js +33 -0
  29. data/{javascripts → lib/netzke/grid_panel_extras/javascripts}/filters.js +0 -0
  30. data/lib/netzke/grid_panel_extras/javascripts/rows-dd.js +280 -0
  31. data/lib/netzke/grid_panel_js.rb +721 -0
  32. data/lib/netzke/panel.rb +13 -0
  33. data/lib/netzke/plugins/configuration_tool.rb +121 -0
  34. data/lib/netzke/property_editor.rb +105 -0
  35. data/lib/netzke/property_editor_extras/helper_model.rb +126 -0
  36. data/lib/netzke/search_panel.rb +62 -0
  37. data/lib/netzke/tab_panel.rb +160 -0
  38. data/lib/netzke/table_editor.rb +118 -0
  39. data/lib/netzke/tree_panel.rb +73 -0
  40. data/lib/netzke/wrapper.rb +42 -0
  41. data/lib/netzke-basepack.rb +9 -15
  42. data/netzke-basepack.gemspec +147 -20
  43. data/stylesheets/basepack.css +26 -0
  44. data/test/app_root/app/models/book.rb +1 -1
  45. data/test/app_root/config/environment.rb +1 -0
  46. data/test/app_root/db/migrate/20081222033440_create_genres.rb +1 -0
  47. data/test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb +1 -1
  48. data/test/app_root/db/migrate/20090102223630_create_netzke_layouts.rb +14 -0
  49. data/test/app_root/vendor/plugins/acts_as_list/README +23 -0
  50. data/test/app_root/vendor/plugins/acts_as_list/init.rb +3 -0
  51. data/test/app_root/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb +256 -0
  52. data/test/test_helper.rb +1 -2
  53. data/test/unit/accordion_panel_test.rb +20 -0
  54. data/test/unit/active_record_basepack_test.rb +54 -0
  55. data/test/unit/grid_panel_test.rb +43 -0
  56. data/test/unit/helper_model_test.rb +30 -0
  57. data/test/unit/netzke_basepack_test.rb +4 -0
  58. data/test/unit/tab_panel_test.rb +21 -0
  59. metadata +96 -72
  60. data/CHANGELOG +0 -14
  61. data/Manifest +0 -65
  62. data/README.mdown +0 -18
  63. data/generators/netzke_basepack/USAGE +0 -8
  64. data/generators/netzke_basepack/netzke_basepack_generator.rb +0 -8
  65. data/generators/netzke_basepack/netzke_grid_generator.rb +0 -7
  66. data/generators/netzke_basepack/templates/create_netzke_grid_columns.rb +0 -21
  67. data/lib/app/models/netzke_grid_column.rb +0 -23
  68. data/lib/netzke/accordion.rb +0 -11
  69. data/lib/netzke/ar_ext.rb +0 -163
  70. data/lib/netzke/column.rb +0 -43
  71. data/lib/netzke/container.rb +0 -81
  72. data/lib/netzke/grid.rb +0 -120
  73. data/lib/netzke/grid_interface.rb +0 -156
  74. data/lib/netzke/grid_js_builder.rb +0 -276
  75. data/lib/netzke/preference_grid.rb +0 -43
  76. data/lib/netzke/properties_tool.rb +0 -66
  77. data/lib/netzke/property_grid.rb +0 -60
  78. data/test/ar_ext_test.rb +0 -39
  79. data/test/column_test.rb +0 -27
  80. data/test/grid_test.rb +0 -43
  81. data/test/netzke_basepack_test.rb +0 -8
@@ -0,0 +1,56 @@
1
+ require 'acts_as_list'
2
+ class NetzkeAutoColumn < ActiveRecord::Base
3
+
4
+ acts_as_list
5
+ default_scope :order => "position"
6
+
7
+ # Returns an array of column configuration hashes (without the "id" attribute)
8
+ def self.all_columns
9
+ self.all.map do |c|
10
+ column_hash = c.attributes.reject{ |k,v| k == 'id' }
11
+ column_hash.each_pair do |k,v|
12
+ # try to detect JSON format
13
+ begin
14
+ normalized_value = v.is_a?(String) ? ActiveSupport::JSON.decode(v) : v
15
+ rescue ActiveSupport::JSON::ParseError
16
+ normalized_value = v
17
+ end
18
+ column_hash[k] = normalized_value
19
+ end
20
+ column_hash
21
+ end
22
+ end
23
+
24
+ # Build the table with columns for this widget
25
+ def self.rebuild_table
26
+ connection.drop_table('netzke_auto_columns') if table_exists?
27
+
28
+ normalized_config_columns = []
29
+
30
+ @@widget.class.config_columns.each do |mc|
31
+ column_hash = mc.is_a?(Symbol) ? {:name => mc} : mc
32
+ column_hash[:type] ||= :string
33
+ normalized_config_columns << column_hash
34
+ end
35
+
36
+ # create the table with the fields
37
+ self.connection.create_table('netzke_auto_columns') do |t|
38
+ normalized_config_columns.each do |mc|
39
+ t.column mc[:name], mc[:type], :default => mc[:default]
40
+ end
41
+ t.column :position, :integer
42
+ end
43
+
44
+ # populate the table with data
45
+ NetzkeAutoColumn.create @@widget.normalized_columns.map(&:deebeefy_values)
46
+
47
+ end
48
+
49
+ def self.widget=(widget)
50
+ @@widget = widget
51
+ if Netzke::Base.session["netzke_auto_column_last_widget"] != @@widget.id_name
52
+ rebuild_table
53
+ Netzke::Base.session["netzke_auto_column_last_widget"] = @@widget.id_name
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,113 @@
1
+ module Netzke
2
+ # == AccordionPanel
3
+ #
4
+ # == Features:
5
+ # * Dynamically loads widgets for the panels that get expanded for the first time
6
+ # * Is loaded along with the active widget - saves a request to the server
7
+ #
8
+ # Future features:
9
+ # * Stores the last active panel in persistent_config
10
+ class AccordionPanel < Base
11
+
12
+ # JavaScript part
13
+ def self.js_extend_properties
14
+ {
15
+ :layout => 'accordion',
16
+ :defaults => {:layout => 'fit'},
17
+ :init_component => <<-END_OF_JAVASCRIPT.l,
18
+ function(){
19
+ Ext.netzke.cache.#{short_widget_class_name}.superclass.initComponent.call(this);
20
+
21
+ // Set events
22
+ this.items.each(function(i){
23
+ // Set the expand event
24
+ i.on('expand', this.loadItemWidget, this);
25
+
26
+ // If not collapsed, add the active aggregatee (item) into it
27
+ if (!i.collapsed) {
28
+ var preloadedItemConfig = this[i.widget.camelize(true) + "Config"];
29
+ i.add(new Ext.netzke.cache[preloadedItemConfig.widgetClassName](preloadedItemConfig));
30
+ i.doLayout(); // always needed after adding a component
31
+ }
32
+ }, this);
33
+ }
34
+ END_OF_JAVASCRIPT
35
+
36
+ # Loads widget into the panel if it wasn't loaded yet
37
+ :load_item_widget => <<-END_OF_JAVASCRIPT.l,
38
+ function(panel) {
39
+ // if (!panel.getWidget()) panel.loadWidget(this.id + "__" + panel.widget + "__get_widget");
40
+ var preloadedItemConfig = this[panel.widget.camelize(true) + "Config"];
41
+
42
+ if (preloadedItemConfig){
43
+ // preloaded widget only needs to be instantiated, as its class and configuration have already been loaded
44
+ panel.add(new Ext.netzke.cache[preloadedItemConfig.widgetClassName](preloadedItemConfig));
45
+ panel.doLayout(); // always needed after adding a component
46
+ } else {
47
+ // load the widget from the server
48
+ this.loadAggregatee({id:panel.widget, container:panel.id});
49
+ }
50
+
51
+ }
52
+ END_OF_JAVASCRIPT
53
+ }
54
+ end
55
+
56
+ # Some normalization of config
57
+ def initialize(*args)
58
+ super
59
+
60
+ seen_active = false
61
+
62
+ config[:items].each_with_index do |item, i|
63
+ # if some items are provided without names, give them generated names
64
+ item[:name] ||= "item#{i}"
65
+
66
+ # remove duplucated :active configuration
67
+ if item[:active]
68
+ item[:active] = nil if seen_active
69
+ seen_active ||= true
70
+ end
71
+ end
72
+ end
73
+
74
+ # Returns items configs
75
+ def items
76
+ @items ||= config[:items]
77
+ end
78
+
79
+ # Provides configs for fit panels (which will effectively be accordion panels)
80
+ def js_config
81
+ super.merge({
82
+ # these "items" are not related to the "items" of the config, rather these are the items required by the the accordion panel
83
+ :items => fit_panels
84
+ })
85
+ end
86
+
87
+ # "Fit-panels" - panels of layout 'fit' (effectively the accordion panels) that will contain the widgets ("items")
88
+ def fit_panels
89
+ res = []
90
+ config[:items].each_with_index do |item, i|
91
+ res << {
92
+ # :id => item[:active] && id_name + '_active', # to mark the fit-panel which will contain the active widget
93
+ :title => item[:title] || (item[:name] && item[:name].to_s.humanize),
94
+ :widget => item[:name], # to know which fit panel will load which widget
95
+ :collapsed => !(item[:active] || false)
96
+ }
97
+ end
98
+ res
99
+ end
100
+
101
+ # All items become *late* aggregatees, besides the ones that are marked "active"
102
+ def initial_aggregatees
103
+ res = {}
104
+ config[:items].each_with_index do |item, i|
105
+ item[:late_aggregation] = !item[:active]
106
+ res.merge!(item[:name].to_sym => item)
107
+ end
108
+ res
109
+ end
110
+
111
+
112
+ end
113
+ end
@@ -0,0 +1,104 @@
1
+ module Netzke::ActiveRecord
2
+ # Provides extensions to all ActiveRecord-based classes
3
+ module Basepack
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ # Allow nested association access (assocs separated by "." or "__"), e.g.: proxy_service.asset__gui_folder__name
9
+ # Example:
10
+ #
11
+ # Book.first.genre__name = 'Fantasy'
12
+ #
13
+ # is the same as:
14
+ #
15
+ # Book.first.genre = Genre.find_by_name('Fantasy')
16
+ #
17
+ # The result - easier forms and grids that handle nested models: simply specify column/field name as "genre__name".
18
+ def method_missing(method, *args, &block)
19
+ # if refering to a column, just pass it to the original method_missing
20
+ return super if self.class.column_names.include?(method.to_s)
21
+
22
+ split = method.to_s.split(/\.|__/)
23
+ if split.size > 1
24
+ if split.last =~ /=$/
25
+ if split.size == 2
26
+ # search for association and assign it to self
27
+ assoc = self.class.reflect_on_association(split.first.to_sym)
28
+ assoc_method = split.last.chop
29
+ if assoc
30
+ begin
31
+ assoc_instance = assoc.klass.send("find_by_#{assoc_method}", *args)
32
+ rescue NoMethodError
33
+ assoc_instance = nil
34
+ logger.debug "!!! no find_by_#{assoc_method} method for class #{assoc.klass.name}\n"
35
+ end
36
+ if (assoc_instance)
37
+ self.send("#{split.first}=", assoc_instance)
38
+ else
39
+ logger.debug "!!! Couldn't find association #{split.first} by #{assoc_method} '#{args.first}'"
40
+ end
41
+ else
42
+ super
43
+ end
44
+ else
45
+ super
46
+ end
47
+ else
48
+ res = self
49
+ split.each do |m|
50
+ if res.respond_to?(m)
51
+ res = res.send(m) unless res.nil?
52
+ else
53
+ res.nil? ? nil : super
54
+ end
55
+ end
56
+ res
57
+ end
58
+ else
59
+ super
60
+ end
61
+ end
62
+
63
+ module ClassMethods
64
+
65
+ def options_for(column, query = nil)
66
+ # First, check if we have options for this class and column defined in persistent storage
67
+ NetzkePreference.widget_name = self.name
68
+ options = NetzkePreference[:combobox_options] || {}
69
+ if options[column]
70
+ options[column].select{ |o| o.index(/^#{query}/) }
71
+ elsif respond_to?("#{column}_combobox_options")
72
+ # AR class provides the choices itself
73
+ send("#{column}_combobox_options", query)
74
+ else
75
+ # Returns all unique values for a column, filtered with <tt>query</tt>
76
+ if (assoc_name, *assoc_method = column.split('__')).size > 1
77
+ # column is an association column
78
+ assoc_method = assoc_method.join('__') # in case we get something like country__continent__name
79
+ association = reflect_on_association(assoc_name.to_sym) || raise(NameError, "Association #{assoc_name} not known for class #{name}")
80
+ association.klass.options_for(assoc_method, query)
81
+ else
82
+ column = assoc_name
83
+ if self.column_names.include?(column)
84
+ # it's simply a column in the table
85
+ records = query.nil? ? find_by_sql("select distinct #{column} from #{table_name}") : find_by_sql("select distinct #{column} from #{table_name} where #{column} like '#{query}%'")
86
+ records.map{|r| r.send(column)}
87
+ else
88
+ # it's a "virtual" column - the least effective search
89
+ records = self.find(:all).map{|r| r.send(column)}.uniq
90
+ query.nil? ? records : records.select{|r| r.index(/^#{query}/)}
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+ end
100
+
101
+ # Extend ActiveRecord
102
+ ActiveRecord::Base.class_eval do
103
+ include Netzke::ActiveRecord::Basepack
104
+ end
@@ -0,0 +1,21 @@
1
+ module Netzke::ActiveRecord
2
+ # Provides extensions to those ActiveRecord-based models that provide data to the "data accessor" widgets,
3
+ # like GridPanel, FormPanel, etc
4
+ module DataAccessor
5
+ # Transforms a record to array of values according to the passed columns.
6
+ def to_array(columns)
7
+ res = []
8
+ for c in columns
9
+ nc = c.is_a?(Symbol) ? {:name => c} : c
10
+ begin
11
+ res << send(nc[:name]) unless nc[:excluded]
12
+ rescue
13
+ # So that we don't crash at a badly configured column
14
+ res << "UNDEF"
15
+ end
16
+ end
17
+ res
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,325 @@
1
+ module Netzke
2
+ # == BasicApp
3
+ # Basis for a Ext.Viewport-based application
4
+ #
5
+ # Features:
6
+ # * dynamic loading of widgets
7
+ # * authentification support
8
+ # * browser history support (press the "Back"-button to go to the previously loaded widget)
9
+ # * FeedbackGhost-powered feedback
10
+ # * aggregation of widget's own menus
11
+ # * masquerade support
12
+ # * AJAX activity indicator
13
+ class BasicApp < Base
14
+ module ClassMethods
15
+
16
+ def js_base_class
17
+ "Ext.Viewport"
18
+ end
19
+
20
+ # Global BasicApp configuration
21
+ def config
22
+ set_default_config({
23
+ :logout_url => "/logout" # default logout url
24
+ })
25
+ end
26
+
27
+ def js_panels
28
+ # In status bar we want to show what we are masquerading as
29
+ if session[:masq_user]
30
+ user = User.find(session[:masq_user])
31
+ masq = %Q{user "#{user.login}"}
32
+ elsif session[:masq_role]
33
+ role = Role.find(session[:masq_role])
34
+ masq = %Q{role "#{role.name}"}
35
+ end
36
+
37
+ [{
38
+ :id => 'main-panel',
39
+ :region => 'center',
40
+ :layout => 'fit'
41
+ },{
42
+ :id => 'main-toolbar',
43
+ :xtype => 'toolbar',
44
+ :region => 'north',
45
+ :height => 25
46
+ # :items => ["-"]
47
+ },{
48
+ :id => 'main-statusbar',
49
+ :xtype => 'statusbar',
50
+ :region => 'south',
51
+ :statusAlign => 'right',
52
+ :busyText => 'Busy...',
53
+ :default_text => masq.nil? ? "Ready #{"(config mode)" if session[:config_mode]}" : "Masquerading as #{masq}",
54
+ :default_icon_cls => ""
55
+ }]
56
+ end
57
+
58
+ def js_extend_properties
59
+ {
60
+ :layout => 'border',
61
+
62
+ :panels => js_panels,
63
+
64
+ :init_component => <<-END_OF_JAVASCRIPT.l,
65
+ function(){
66
+ this.items = this.panels; // a bit weird, but working; can't assign it straight
67
+
68
+ Ext.netzke.cache.BasicApp.superclass.initComponent.call(this);
69
+
70
+ // If we are given a token, load the corresponding widget, otherwise load the last loaded widget
71
+ var currentToken = Ext.History.getToken();
72
+ if (currentToken != "") {
73
+ this.processHistory(currentToken)
74
+ } else {
75
+ var lastLoaded = this.initialConfig.widgetToLoad; // passed from the server
76
+ if (lastLoaded) Ext.History.add(lastLoaded);
77
+ }
78
+
79
+ Ext.History.on('change', this.processHistory, this);
80
+
81
+ // Hosted menus
82
+ this.menus = {};
83
+
84
+ // Setting the "busy" indicator for Ajax requests
85
+ Ext.Ajax.on('beforerequest', function(){this.findById('main-statusbar').showBusy()}, this);
86
+ Ext.Ajax.on('requestcomplete', function(){this.findById('main-statusbar').hideBusy()}, this);
87
+ Ext.Ajax.on('requestexception', function(){this.findById('main-statusbar').hideBusy()}, this);
88
+ }
89
+ END_OF_JAVASCRIPT
90
+
91
+ :host_menu => <<-END_OF_JAVASCRIPT.l,
92
+ function(menu, owner){
93
+ var toolbar = this.findById('main-toolbar');
94
+ if (!this.menus[owner.id]) this.menus[owner.id] = [];
95
+ Ext.each(menu, function(item) {
96
+ // var newMenu = new Ext.Toolbar.Button(item);
97
+ // var position = toolbar.items.getCount() - 2;
98
+ // position = position < 0 ? 0 : position;
99
+ // toolbar.insertButton(position, newMenu);
100
+
101
+ toolbar.add(item);
102
+ // this.menus[owner.id].push(newMenu); // TODO: remember the menus from this owner in some other way
103
+ }, this);
104
+ }
105
+ END_OF_JAVASCRIPT
106
+
107
+ :unhost_menu => <<-END_OF_JAVASCRIPT.l,
108
+ function(owner){
109
+ // var toolbar = this.findById('main-toolbar');
110
+ // if (this.menus[owner.id]) {
111
+ // Ext.each(this.menus[owner.id], function(menu){
112
+ // toolbar.items.remove(menu); // remove the item from the toolbar
113
+ // menu.destroy(); // ... and destroy it
114
+ // });
115
+ // }
116
+ }
117
+ END_OF_JAVASCRIPT
118
+
119
+ :logout => <<-END_OF_JAVASCRIPT.l,
120
+ function(){
121
+ window.location = "#{config[:logout_url]}"
122
+ }
123
+ END_OF_JAVASCRIPT
124
+
125
+ # Event handler for history change
126
+ :process_history => <<-END_OF_JAVASCRIPT.l,
127
+ function(token){
128
+ if (token){
129
+ this.loadAggregatee({id:token, container:'main-panel'});
130
+ } else {
131
+ }
132
+ }
133
+ END_OF_JAVASCRIPT
134
+
135
+ :instantiate_aggregatee => <<-END_OF_JAVASCRIPT.l,
136
+ function(config){
137
+ this.findById('main-panel').instantiateChild(config);
138
+ }
139
+ END_OF_JAVASCRIPT
140
+
141
+ # Loads widget by name
142
+ :app_load_widget => <<-END_OF_JAVASCRIPT.l,
143
+ function(name){
144
+ Ext.History.add(name);
145
+ }
146
+ END_OF_JAVASCRIPT
147
+
148
+ # Loads widget by action
149
+ :load_widget_by_action => <<-END_OF_JAVASCRIPT.l,
150
+ function(action){
151
+ this.appLoadWidget(action.widget || action.name);
152
+ }
153
+ END_OF_JAVASCRIPT
154
+
155
+ # Masquerade selector window
156
+ :show_masquerade_selector => <<-END_OF_JAVASCRIPT.l,
157
+ function(){
158
+ var w = new Ext.Window({
159
+ title: 'Masquerade as',
160
+ modal: true,
161
+ width: Ext.lib.Dom.getViewWidth() * 0.6,
162
+ height: Ext.lib.Dom.getViewHeight() * 0.6,
163
+ layout: 'fit',
164
+ closeAction :'destroy',
165
+ buttons: [{
166
+ text: 'Select',
167
+ handler : function(){
168
+ if (role = w.getWidget().masquerade.role) {
169
+ Ext.Msg.confirm("Masquerading as a role", "Individual preferences for all users with this role will get overwritten as you make changes. Continue?", function(btn){
170
+ if (btn === 'yes') {
171
+ w.close();
172
+ }
173
+ });
174
+ } else {
175
+ w.close();
176
+ }
177
+ },
178
+ scope:this
179
+ },{
180
+ text:'Turn off masquerading',
181
+ handler:function(){
182
+ this.masquerade = {};
183
+ w.close();
184
+ },
185
+ scope:this
186
+ },{
187
+ text:'Cansel',
188
+ handler:function(){
189
+ w.hide();
190
+ },
191
+ scope:this
192
+ }],
193
+ listeners : {close: {fn: function(){
194
+ this.masqAs(this.masquerade || w.getWidget().masquerade || {});
195
+ }, scope: this}}
196
+ });
197
+
198
+ w.show(null, function(){
199
+ this.loadAggregatee({id:"masqueradeSelector", container:w.id})
200
+ }, this);
201
+
202
+ }
203
+ END_OF_JAVASCRIPT
204
+
205
+ # Masquerade as...
206
+ :masq_as => <<-END_OF_JAVASCRIPT.l
207
+ function(masqConfig){
208
+ params = {};
209
+
210
+ if (masqConfig.user) {
211
+ params.user = masqConfig.user
212
+ }
213
+
214
+ if (masqConfig.role) {
215
+ params.role = masqConfig.role
216
+ }
217
+
218
+ this.masqueradeAs(params);
219
+
220
+ }
221
+ END_OF_JAVASCRIPT
222
+ }
223
+ end
224
+ end
225
+
226
+ extend ClassMethods
227
+
228
+ # Set the Logout button if Netzke::Base.user is set
229
+ def menu
230
+ res = []
231
+ user = Netzke::Base.user
232
+ if !user.nil?
233
+ user_name = user.respond_to?(:name) ? user.name : user.login # try to display user's name, fallback to login
234
+ res << "->" <<
235
+ {
236
+ :text => "#{user_name}",
237
+ :menu => user_menu
238
+ }
239
+ else
240
+ res << "->" <<
241
+ {
242
+ :text => "Login",
243
+ :handler => <<-END_OF_JAVASCRIPT.l,
244
+ function(){
245
+ window.location = "/login"
246
+ }
247
+ END_OF_JAVASCRIPT
248
+ :scope => this
249
+ }
250
+ end
251
+ res
252
+ end
253
+
254
+ def user_menu
255
+ ['logout']
256
+ end
257
+
258
+ def initialize(*args)
259
+ super
260
+
261
+ if session[:netzke_just_logged_in] || session[:netzke_just_logged_out]
262
+ session[:config_mode] = false
263
+ session[:masq_user] = session[:masq_roles] = nil
264
+ end
265
+
266
+ strong_children_config.deep_merge!({:ext_config => {:mode => :config}}) if session[:config_mode]
267
+ end
268
+
269
+ #
270
+ # Available actions
271
+ #
272
+ def actions
273
+ {
274
+ :masquerade_selector => {:text => "Masquerade as ...", :fn => "showMasqueradeSelector"},
275
+ :toggle_config_mode => {:text => "#{session[:config_mode] ? "Leave" : "Enter"} config mode", :fn => "toggleConfigMode"},
276
+ :logout => {:text => "Log out", :fn => "logout"}
277
+ }
278
+ end
279
+
280
+
281
+ # Html required for Ext.History to work
282
+ def js_widget_html
283
+ super << %Q{
284
+ <form id="history-form" class="x-hidden">
285
+ <input type="hidden" id="x-history-field" />
286
+ <iframe id="x-history-frame"></iframe>
287
+ </form>
288
+ }
289
+ end
290
+
291
+ # We rely on the FeedbackGhost (to not need to implement our own feedback management)
292
+ def initial_aggregatees
293
+ {:feedback_ghost => {:widget_class_name => "FeedbackGhost"}}
294
+ end
295
+
296
+ # Besides instantiating ourselves, also instantiate the FeedbackGhost
297
+ def js_widget_instance
298
+ <<-END_OF_JAVASCRIPT << super
299
+ new Ext.netzke.cache['FeedbackGhost']({id:'feedback_ghost'})
300
+ // Initialize history (can't say why it's not working well inside the appLoaded handler)
301
+ Ext.History.init();
302
+ END_OF_JAVASCRIPT
303
+ end
304
+
305
+ #
306
+ # Interface section
307
+ #
308
+
309
+ api :toggle_config_mode
310
+ def toggle_config_mode(params)
311
+ session = Netzke::Base.session
312
+ session[:config_mode] = !session[:config_mode]
313
+ {:js => "window.location.reload();"}
314
+ end
315
+
316
+ api :masquerade_as
317
+ def masquerade_as(params)
318
+ session = Netzke::Base.session
319
+ session[:masq_role] = params[:role]
320
+ session[:masq_user] = params[:user]
321
+ {:js => "window.location.reload()"}
322
+ end
323
+
324
+ end
325
+ end