skozlov-netzke-basepack 0.1.1.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +1 -0
- data/.gitignore +5 -0
- data/LICENSE +2 -19
- data/README.rdoc +87 -0
- data/Rakefile +28 -12
- data/TODO.rdoc +7 -0
- data/VERSION +1 -0
- data/autotest/discover.rb +3 -0
- data/init.rb +0 -1
- data/javascripts/basepack.js +839 -49
- data/lib/app/models/netzke_auto_column.rb +56 -0
- data/lib/netzke/accordion_panel.rb +113 -0
- data/lib/netzke/active_record/basepack.rb +104 -0
- data/lib/netzke/active_record/data_accessor.rb +21 -0
- data/lib/netzke/basic_app.rb +325 -0
- data/lib/netzke/border_layout_panel.rb +128 -0
- data/lib/netzke/configuration_panel.rb +24 -0
- data/lib/netzke/data_accessor.rb +71 -0
- data/lib/netzke/ext.rb +6 -0
- data/lib/netzke/field_model.rb +131 -0
- data/lib/netzke/fields_configurator.rb +95 -0
- data/lib/netzke/form_panel.rb +214 -0
- data/lib/netzke/form_panel_api.rb +74 -0
- data/lib/netzke/form_panel_extras/javascripts/xcheckbox.js +82 -0
- data/lib/netzke/form_panel_js.rb +129 -0
- data/lib/netzke/grid_panel.rb +442 -0
- data/lib/netzke/grid_panel_api.rb +352 -0
- data/lib/netzke/grid_panel_extras/javascripts/check-column.js +33 -0
- data/{javascripts → lib/netzke/grid_panel_extras/javascripts}/filters.js +0 -0
- data/lib/netzke/grid_panel_extras/javascripts/rows-dd.js +280 -0
- data/lib/netzke/grid_panel_js.rb +721 -0
- data/lib/netzke/panel.rb +13 -0
- data/lib/netzke/plugins/configuration_tool.rb +121 -0
- data/lib/netzke/property_editor.rb +105 -0
- data/lib/netzke/property_editor_extras/helper_model.rb +126 -0
- data/lib/netzke/search_panel.rb +62 -0
- data/lib/netzke/tab_panel.rb +160 -0
- data/lib/netzke/table_editor.rb +118 -0
- data/lib/netzke/tree_panel.rb +73 -0
- data/lib/netzke/wrapper.rb +42 -0
- data/lib/netzke-basepack.rb +9 -15
- data/netzke-basepack.gemspec +147 -20
- data/stylesheets/basepack.css +26 -0
- data/test/app_root/app/models/book.rb +1 -1
- data/test/app_root/config/environment.rb +1 -0
- data/test/app_root/db/migrate/20081222033440_create_genres.rb +1 -0
- data/test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb +1 -1
- data/test/app_root/db/migrate/20090102223630_create_netzke_layouts.rb +14 -0
- data/test/app_root/vendor/plugins/acts_as_list/README +23 -0
- data/test/app_root/vendor/plugins/acts_as_list/init.rb +3 -0
- data/test/app_root/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb +256 -0
- data/test/test_helper.rb +1 -2
- data/test/unit/accordion_panel_test.rb +20 -0
- data/test/unit/active_record_basepack_test.rb +54 -0
- data/test/unit/grid_panel_test.rb +43 -0
- data/test/unit/helper_model_test.rb +30 -0
- data/test/unit/netzke_basepack_test.rb +4 -0
- data/test/unit/tab_panel_test.rb +21 -0
- metadata +96 -72
- data/CHANGELOG +0 -14
- data/Manifest +0 -65
- data/README.mdown +0 -18
- data/generators/netzke_basepack/USAGE +0 -8
- data/generators/netzke_basepack/netzke_basepack_generator.rb +0 -8
- data/generators/netzke_basepack/netzke_grid_generator.rb +0 -7
- data/generators/netzke_basepack/templates/create_netzke_grid_columns.rb +0 -21
- data/lib/app/models/netzke_grid_column.rb +0 -23
- data/lib/netzke/accordion.rb +0 -11
- data/lib/netzke/ar_ext.rb +0 -163
- data/lib/netzke/column.rb +0 -43
- data/lib/netzke/container.rb +0 -81
- data/lib/netzke/grid.rb +0 -120
- data/lib/netzke/grid_interface.rb +0 -156
- data/lib/netzke/grid_js_builder.rb +0 -276
- data/lib/netzke/preference_grid.rb +0 -43
- data/lib/netzke/properties_tool.rb +0 -66
- data/lib/netzke/property_grid.rb +0 -60
- data/test/ar_ext_test.rb +0 -39
- data/test/column_test.rb +0 -27
- data/test/grid_test.rb +0 -43
- 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
|