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.
- 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
|