netzke-basepack 0.5.8 → 0.5.9
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/CHANGELOG.rdoc +18 -0
- data/README.rdoc +11 -4
- data/Rakefile +19 -3
- data/TODO.rdoc +1 -1
- data/generators/netzke_basepack/netzke_basepack_generator.rb +13 -0
- data/generators/netzke_basepack/templates/create_netzke_field_lists.rb +18 -0
- data/generators/netzke_basepack/templates/public_assets/ts-checkbox.gif +0 -0
- data/install.rb +1 -1
- data/javascripts/basepack.js +124 -30
- data/lib/app/models/netzke_field_list.rb +261 -0
- data/lib/app/models/netzke_model_attr_list.rb +21 -0
- data/lib/app/models/netzke_persistent_array_auto_model.rb +58 -0
- data/lib/netzke-basepack.rb +17 -2
- data/lib/netzke/active_record.rb +10 -0
- data/lib/netzke/active_record/association_attributes.rb +102 -0
- data/lib/netzke/active_record/attributes.rb +100 -0
- data/lib/netzke/active_record/combobox_options.rb +43 -0
- data/lib/netzke/active_record/data_accessor.rb +9 -12
- data/lib/netzke/attributes_configurator.rb +195 -0
- data/lib/netzke/basic_app.rb +47 -4
- data/lib/netzke/configuration_panel.rb +1 -1
- data/lib/netzke/data_accessor.rb +7 -30
- data/lib/netzke/fields_configurator.rb +106 -41
- data/lib/netzke/form_panel.rb +28 -125
- data/lib/netzke/form_panel/form_panel_api.rb +2 -3
- data/lib/netzke/form_panel/form_panel_fields.rb +147 -0
- data/lib/netzke/form_panel/form_panel_js.rb +35 -15
- data/lib/netzke/grid_panel.rb +130 -213
- data/lib/netzke/grid_panel/grid_panel_api.rb +254 -257
- data/lib/netzke/grid_panel/grid_panel_columns.rb +226 -0
- data/lib/netzke/grid_panel/grid_panel_js.rb +126 -119
- data/lib/netzke/grid_panel/record_form_window.rb +7 -1
- data/lib/netzke/json_array_editor.rb +61 -0
- data/lib/netzke/plugins/configuration_tool.rb +1 -1
- data/lib/netzke/property_editor.rb +3 -3
- data/lib/netzke/search_panel.rb +164 -27
- data/lib/netzke/tab_panel.rb +14 -12
- data/stylesheets/basepack.css +43 -2
- data/test/app_root/app/models/book.rb +1 -1
- data/test/app_root/app/models/role.rb +3 -0
- data/test/app_root/app/models/user.rb +3 -0
- data/test/app_root/config/database.yml +1 -1
- data/test/app_root/db/migrate/20090102223630_create_netzke_field_lists.rb +18 -0
- data/test/app_root/db/migrate/20090423214303_create_roles.rb +11 -0
- data/test/app_root/db/migrate/20090423222114_create_users.rb +12 -0
- data/test/fixtures/books.yml +4 -2
- data/test/fixtures/categories.yml +2 -2
- data/test/fixtures/genres.yml +6 -6
- data/test/fixtures/roles.yml +8 -0
- data/test/fixtures/users.yml +11 -0
- data/test/test_helper.rb +2 -0
- data/test/unit/active_record_basepack_test.rb +2 -2
- data/test/unit/fields_configuration_test.rb +18 -0
- data/test/unit/grid_panel_test.rb +29 -27
- metadata +41 -16
- data/lib/app/models/netzke_auto_column.rb +0 -4
- data/lib/app/models/netzke_auto_field.rb +0 -4
- data/lib/app/models/netzke_auto_table.rb +0 -61
- data/lib/netzke/active_record/basepack.rb +0 -134
- data/lib/netzke/grid_panel/javascripts/filters.js +0 -7
- data/test/app_root/db/migrate/20090102223630_create_netzke_layouts.rb +0 -14
- data/test/unit/helper_model_test.rb +0 -30
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
= v0.6.0 - EDGE
|
2
|
+
* !!!: after updating to this version, run script/generate netzke_basepack
|
3
|
+
* New: tri-state checkbox introduced into the search form
|
4
|
+
* Impr: better defaults for the search form
|
5
|
+
* Impr: GridPanel now displays the total amount of records
|
6
|
+
* Fix: GridPanel's "local filters" are back (they were out since the release of Ext 3.0)
|
7
|
+
* Impr: obey "preloaded" option for tabs better: really add the widgets into their respective tabs from the start
|
8
|
+
* Fix: datetime filters now also are take into account when searching in GridPanel is performed
|
9
|
+
* Fix: FieldsConfigurator should now work on Heroku
|
10
|
+
* Fix: GridPanel date filter now should work
|
11
|
+
* New: GridPanel now has a method on_data_changed that can be overridden by children to detect actions that modify data
|
12
|
+
* New: New way of Netzke virtual attributes in AR models.
|
13
|
+
* New: Grid/FormPanel refactoring.
|
14
|
+
* Impr: Multi-level column/fields configuration reworked and made more consistent.
|
15
|
+
* New: New configuration layer introduced between the AR models and the rest of Netzke widgets. Use AttributesConfigurator to access it.
|
16
|
+
* New: FamFamFam Silk icons support.
|
17
|
+
* Impr: Reworked Netzke (virtual) attributes for Grid/FormPanel
|
18
|
+
|
1
19
|
= v0.5.8 - 2010-03-12
|
2
20
|
* Fix: GertThiel's method_missing-related fix enabling better compatibility with other Ruby libs
|
3
21
|
* Fix: acts_as_list runtime dependency
|
data/README.rdoc
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
= netzke-basepack
|
2
2
|
A pack of basic Rails/ExtJS widgets as a part of the Netzke framework. Live demo/tutorials on http://blog.writelesscode.com. Introduction to the Netzke framework and the Wiki: http://github.com/skozlov/netzke
|
3
3
|
|
4
|
-
|
4
|
+
== Prerequisites
|
5
5
|
1. Rails >= 2.2
|
6
6
|
2. Netzke assumes that your ExtJS library is in public/extjs, which may be a symbolic link, e.g:
|
7
7
|
|
@@ -28,7 +28,7 @@ Or install as gem:
|
|
28
28
|
|
29
29
|
gem install netzke-basepack
|
30
30
|
|
31
|
-
|
31
|
+
== Usage
|
32
32
|
If using as gem, include it into environment.rb:
|
33
33
|
|
34
34
|
config.gem "netzke-basepack"
|
@@ -37,10 +37,14 @@ Include mapping for Netzke controller providing Netzke javascripts and styles (i
|
|
37
37
|
|
38
38
|
map.netzke
|
39
39
|
|
40
|
-
|
40
|
+
Run the generators (some assets and a migration for dynamically configured columns/fields):
|
41
|
+
|
42
|
+
./script/generate netzke_basepack
|
41
43
|
|
42
|
-
|
44
|
+
To be able to use persistent on-the-fly configuration of widgets, run netzke-core generators:
|
43
45
|
|
46
|
+
./script/generate netzke_core
|
47
|
+
|
44
48
|
... then run the migrations:
|
45
49
|
|
46
50
|
rake db:migrate
|
@@ -64,6 +68,9 @@ Now embed a widget into a view like this:
|
|
64
68
|
|
65
69
|
For more examples, see http://netzke-demo.writelesscode.com
|
66
70
|
|
71
|
+
== Icons support
|
72
|
+
Netzke-basepack supports FamFamFam Silk icon set (http://www.famfamfam.com/archive/silk-icons-thats-your-lot/). To enable it, download it and put the "icons" folder into your app's public/images folder. Then restart your application.
|
73
|
+
|
67
74
|
== More info
|
68
75
|
Introduction to Netzke framework and wiki: http://github.com/skozlov/netzke
|
69
76
|
|
data/Rakefile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
begin
|
2
2
|
require 'jeweler'
|
3
3
|
Jeweler::Tasks.new do |gemspec|
|
4
|
-
gemspec.version = "0.5.
|
4
|
+
gemspec.version = "0.5.9"
|
5
5
|
gemspec.name = "netzke-basepack"
|
6
6
|
gemspec.summary = "Pre-built Rails + ExtJS widgets for your RIA"
|
7
7
|
gemspec.description = "A set of full-featured extendible Netzke widgets (such as FormPanel, GridPanel, Window, BorderLayoutPanel, etc) which can be used as building block for your RIA"
|
@@ -9,14 +9,30 @@ begin
|
|
9
9
|
gemspec.homepage = "http://github.com/skozlov/netzke-basepack"
|
10
10
|
gemspec.rubyforge_project = "netzke-basepack"
|
11
11
|
gemspec.authors = ["Sergei Kozlov"]
|
12
|
-
gemspec.add_dependency("netzke-core", ">=0.5.
|
12
|
+
gemspec.add_dependency("netzke-core", ">=0.5.2")
|
13
13
|
gemspec.add_dependency("searchlogic", ">=2.0.0")
|
14
14
|
gemspec.add_dependency("will_paginate", ">=2.0.0")
|
15
15
|
gemspec.add_dependency("acts_as_list", ">=0.1.2")
|
16
|
+
gemspec.post_install_message = <<-MESSAGE
|
17
|
+
|
18
|
+
========================================================================
|
19
|
+
|
20
|
+
Thanks for installing Netzke Basepack!
|
21
|
+
|
22
|
+
Run "./script/generate netzke_basepack" to finish the installation.
|
23
|
+
|
24
|
+
Netzke home page: http://netzke.org
|
25
|
+
Netzke Google Groups: http://groups.google.com/group/netzke
|
26
|
+
Netzke tutorials: http://blog.writelesscode.com
|
27
|
+
|
28
|
+
========================================================================
|
29
|
+
|
30
|
+
MESSAGE
|
31
|
+
|
16
32
|
end
|
17
33
|
Jeweler::GemcutterTasks.new
|
18
34
|
rescue LoadError
|
19
|
-
puts "Jeweler (or a dependency) not available. Install it with:
|
35
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
36
|
end
|
21
37
|
|
22
38
|
require 'rake/rdoctask'
|
data/TODO.rdoc
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
== Priority
|
2
|
-
* Introduce three-state checkbox for SearchPanel
|
3
2
|
* Add icons to buttons to actions (toolbars/menus)
|
4
3
|
* On grid refresh, reset the dirty fields, so that the "Apply" button doesn't do anything
|
5
4
|
|
6
5
|
== Foolproof
|
7
6
|
* Should not be possible delete the "ID" field from grids/forms
|
7
|
+
* Should not be possible to put the "ID" field on any place but first for grids (otherwise record ID is not correct)
|
8
8
|
|
9
9
|
== Optimizations
|
10
10
|
* Check persistent_config-related queries (aren't they too many?)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# class NetzkeCoreGenerator < Rails::Generator::NamedBase
|
2
|
+
class NetzkeBasepackGenerator < Rails::Generator::Base
|
3
|
+
def manifest
|
4
|
+
record do |m|
|
5
|
+
m.directory 'public/netzke/basepack'
|
6
|
+
m.file 'public_assets/ts-checkbox.gif', "public/netzke/basepack/ts-checkbox.gif"
|
7
|
+
|
8
|
+
m.migration_template 'create_netzke_field_lists.rb', 'db/migrate', :assigns => {
|
9
|
+
:migration_name => "CreateNetzkeFieldLists"
|
10
|
+
}, :migration_file_name => "create_netzke_field_lists"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateNetzkeFieldLists < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :netzke_field_lists do |t|
|
4
|
+
t.string :name
|
5
|
+
t.text :value
|
6
|
+
t.string :model_name
|
7
|
+
t.integer :user_id
|
8
|
+
t.integer :role_id
|
9
|
+
t.string :type
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :netzke_field_lists
|
17
|
+
end
|
18
|
+
end
|
Binary file
|
data/install.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
# Install hook code here
|
1
|
+
# Install hook code here
|
data/javascripts/basepack.js
CHANGED
@@ -6,7 +6,7 @@ Ext.netzke.PassField = Ext.extend(Ext.form.TextField, {
|
|
6
6
|
});
|
7
7
|
Ext.reg('passfield', Ext.netzke.PassField);
|
8
8
|
|
9
|
-
//
|
9
|
+
// ComboBox that gets options from the server (used in both grids and panels)
|
10
10
|
Ext.netzke.ComboBox = Ext.extend(Ext.form.ComboBox, {
|
11
11
|
displayField : 'id',
|
12
12
|
valueField : 'id',
|
@@ -14,29 +14,22 @@ Ext.netzke.ComboBox = Ext.extend(Ext.form.ComboBox, {
|
|
14
14
|
typeAhead : true,
|
15
15
|
|
16
16
|
initComponent : function(){
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
reader : new Ext.data.ArrayReader({root:'data', id:0}, row)
|
27
|
-
});
|
28
|
-
|
29
|
-
Ext.apply(this, {
|
30
|
-
store : store
|
31
|
-
});
|
32
|
-
}
|
17
|
+
var row = Ext.data.Record.create([{name:'id'}]);
|
18
|
+
var store = new Ext.data.Store({
|
19
|
+
proxy : new Ext.data.HttpProxy({url: Ext.getCmp(this.parentId).buildApiUrl("get_combobox_options"), jsonData:{column:this.name}}),
|
20
|
+
reader : new Ext.data.ArrayReader({root:'data', id:0}, row)
|
21
|
+
});
|
22
|
+
|
23
|
+
Ext.apply(this, {
|
24
|
+
store : store
|
25
|
+
});
|
33
26
|
|
34
27
|
Ext.netzke.ComboBox.superclass.initComponent.apply(this, arguments);
|
35
28
|
|
36
29
|
this.on('blur', function(cb){
|
37
30
|
cb.setValue(cb.getRawValue());
|
38
31
|
});
|
39
|
-
|
32
|
+
|
40
33
|
this.on('specialkey', function(cb, event){
|
41
34
|
if (event.getKey() == 9 || event.getKey() == 13) {cb.setValue(cb.getRawValue());}
|
42
35
|
});
|
@@ -85,17 +78,6 @@ Ext.util.Format.mask = function(v){
|
|
85
78
|
return "********";
|
86
79
|
};
|
87
80
|
|
88
|
-
// Mapping of editor field to grid filters
|
89
|
-
Ext.netzke.filterMap = {
|
90
|
-
numberfield:'Numeric',
|
91
|
-
textfield:'String',
|
92
|
-
textarea:'String',
|
93
|
-
xdatetime:'String',
|
94
|
-
checkbox:'Boolean',
|
95
|
-
combobox:'String',
|
96
|
-
datefield:'Date'
|
97
|
-
};
|
98
|
-
|
99
81
|
Ext.data.RecordArrayReader = Ext.extend(Ext.data.JsonReader, {
|
100
82
|
/**
|
101
83
|
* Create a data block containing Ext.data.Records from an Array.
|
@@ -865,4 +847,116 @@ Ext.override(Ext.Panel, {
|
|
865
847
|
this.syncShadow();
|
866
848
|
Ext.Panel.superclass.onResize.call(this, w, h, rw, rh);
|
867
849
|
}
|
868
|
-
});
|
850
|
+
});
|
851
|
+
|
852
|
+
Ext.ns('Ext.ux.form');
|
853
|
+
Ext.ux.form.TriCheckbox = Ext.extend(Ext.form.Checkbox, {
|
854
|
+
checked: null,
|
855
|
+
valueList: [null, false, true],
|
856
|
+
stateClassList: ['x-checkbox-undef', null, 'x-checkbox-checked'],
|
857
|
+
overClass: 'x-form-check-over',
|
858
|
+
clickClass: 'x-form-check-down',
|
859
|
+
triState: true,
|
860
|
+
defaultAutoCreate: {tag: 'input', type: 'hidden', autocomplete: 'off'},
|
861
|
+
initComponent: function() {
|
862
|
+
this.value = this.checked;
|
863
|
+
Ext.ux.form.TriCheckbox.superclass.initComponent.apply(this, arguments);
|
864
|
+
// make a copy before modifying valueList and stateClassList arrays
|
865
|
+
this.vList = this.valueList.slice(0);
|
866
|
+
this.cList = this.stateClassList.slice(0);
|
867
|
+
if(this.triState !== true) {
|
868
|
+
// consider 'undefined' value and its class go first in arrays
|
869
|
+
this.vList.shift();
|
870
|
+
this.cList.shift();
|
871
|
+
}
|
872
|
+
if(this.overCls !== undefined) {
|
873
|
+
this.overClass = this.overCls;
|
874
|
+
delete this.overCls;
|
875
|
+
}
|
876
|
+
this.value = this.normalizeValue(this.value);
|
877
|
+
},
|
878
|
+
onRender : function(ct, position){
|
879
|
+
Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
|
880
|
+
|
881
|
+
this.innerWrap = this.el.wrap({tag: 'span', cls: 'x-form-check-innerwrap'});
|
882
|
+
this.wrap = this.innerWrap.wrap({cls: 'x-form-check-wrap'});
|
883
|
+
|
884
|
+
this.currCls = this.getCls(this.value);
|
885
|
+
this.wrap.addClass(this.currCls);
|
886
|
+
if(this.clickClass && !this.disabled && !this.readOnly)
|
887
|
+
this.innerWrap.addClassOnClick(this.clickClass);
|
888
|
+
if(this.overClass && !this.disabled && !this.readOnly)
|
889
|
+
this.innerWrap.addClassOnOver(this.overClass);
|
890
|
+
|
891
|
+
this.imageEl = this.innerWrap.createChild({
|
892
|
+
tag: 'img',
|
893
|
+
src: Ext.BLANK_IMAGE_URL,
|
894
|
+
cls: 'x-form-tscheckbox'
|
895
|
+
}, this.el);
|
896
|
+
if(this.fieldClass) this.imageEl.addClass(this.fieldClass);
|
897
|
+
|
898
|
+
if(this.boxLabel){
|
899
|
+
this.innerWrap.createChild({
|
900
|
+
tag: 'label',
|
901
|
+
htmlFor: this.el.id,
|
902
|
+
cls: 'x-form-cb-label',
|
903
|
+
html: this.boxLabel
|
904
|
+
});
|
905
|
+
}
|
906
|
+
|
907
|
+
// Need to repaint for IE, otherwise positioning is broken
|
908
|
+
if(Ext.isIE){
|
909
|
+
this.wrap.repaint();
|
910
|
+
}
|
911
|
+
this.resizeEl = this.positionEl = this.wrap;
|
912
|
+
},
|
913
|
+
onResize : function(){
|
914
|
+
Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
|
915
|
+
if(!this.boxLabel && !this.fieldLabel && this.imageEl){
|
916
|
+
this.imageEl.alignTo(this.wrap, 'c-c');
|
917
|
+
}
|
918
|
+
},
|
919
|
+
initEvents : function(){
|
920
|
+
Ext.form.Checkbox.superclass.initEvents.call(this);
|
921
|
+
this.mon(this.innerWrap, {
|
922
|
+
scope: this,
|
923
|
+
click: this.onClick
|
924
|
+
});
|
925
|
+
},
|
926
|
+
onClick : function(){
|
927
|
+
if (!this.disabled && !this.readOnly) {
|
928
|
+
this.setValue(this.vList[(this.vList.indexOf(this.value) + 1) % this.vList.length]);
|
929
|
+
}
|
930
|
+
},
|
931
|
+
getValue : function(){
|
932
|
+
return this.value;
|
933
|
+
},
|
934
|
+
setValue : function(v){
|
935
|
+
var value = this.value;
|
936
|
+
this.value = this.normalizeValue(v);
|
937
|
+
if(this.rendered) this.el.dom.value = this.value;
|
938
|
+
|
939
|
+
if(value !== this.value){
|
940
|
+
this.updateView();
|
941
|
+
this.fireEvent('check', this, this.value);
|
942
|
+
if(this.handler) this.handler.call(this.scope || this, this, this.value);
|
943
|
+
}
|
944
|
+
return this;
|
945
|
+
},
|
946
|
+
normalizeValue: function(v) {
|
947
|
+
return (v === null || v === undefined) && this.triState ? null :
|
948
|
+
(v === true || (['true', 'yes', 'on', '1']).indexOf(String(v).toLowerCase()) != -1);
|
949
|
+
},
|
950
|
+
getCls: function(v) {
|
951
|
+
var idx = this.vList.indexOf(this.value);
|
952
|
+
return idx > -1 ? this.cList[idx] : undefined;
|
953
|
+
},
|
954
|
+
updateView: function() {
|
955
|
+
var cls = this.getCls(this.value);
|
956
|
+
if (!this.wrap || cls === undefined) return;
|
957
|
+
|
958
|
+
this.wrap.replaceClass(this.currCls, cls);
|
959
|
+
this.currCls = cls;
|
960
|
+
}
|
961
|
+
});
|
962
|
+
Ext.reg('tricheckbox', Ext.ux.form.TriCheckbox);
|
@@ -0,0 +1,261 @@
|
|
1
|
+
# TODO: clean up, document and test
|
2
|
+
class NetzkeFieldList < ActiveRecord::Base
|
3
|
+
belongs_to :user
|
4
|
+
belongs_to :role
|
5
|
+
belongs_to :parent, :class_name => "NetzkeFieldList"
|
6
|
+
has_many :children, :class_name => "NetzkeFieldList", :foreign_key => "parent_id"
|
7
|
+
|
8
|
+
|
9
|
+
def self.update_fields(owner_id, attrs_hash)
|
10
|
+
self.find_all_below_current_authority_level(owner_id).each do |list|
|
11
|
+
list.update_attrs(attrs_hash)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Updates attributes in the list
|
16
|
+
def update_attrs(attrs_hash)
|
17
|
+
list = ActiveSupport::JSON.decode(self.value)
|
18
|
+
list.each do |field|
|
19
|
+
field.merge!(attrs_hash[field["name"]]) if attrs_hash[field["name"]]
|
20
|
+
end
|
21
|
+
update_attribute(:value, list.to_json)
|
22
|
+
end
|
23
|
+
|
24
|
+
def append_attr(attr_hash)
|
25
|
+
list = ActiveSupport::JSON.decode(self.value)
|
26
|
+
list << attr_hash
|
27
|
+
update_attribute(:value, list.to_json)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.find_all_below_current_authority_level(pref_name)
|
31
|
+
authority_level, authority_id = Netzke::Base.authority_level
|
32
|
+
case authority_level
|
33
|
+
when :world
|
34
|
+
self.all(:conditions => {:name => pref_name})
|
35
|
+
when :role
|
36
|
+
role = Role.find(authority_id)
|
37
|
+
role.users.inject([]) do |r, user|
|
38
|
+
r += self.all(:conditions => {:user_id => user.id, :name => pref_name})
|
39
|
+
end
|
40
|
+
else
|
41
|
+
[]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.find_all_lists_under_current_authority(model_name)
|
46
|
+
authority_level, authority_id = Netzke::Base.authority_level
|
47
|
+
case authority_level
|
48
|
+
when :world
|
49
|
+
self.all(:conditions => {:model_name => model_name})
|
50
|
+
when :role
|
51
|
+
role = Role.find(authority_id)
|
52
|
+
role.users.inject([]) do |r, user|
|
53
|
+
r += self.all(:conditions => {:user_id => user.id, :model_name => model_name})
|
54
|
+
end
|
55
|
+
when :user
|
56
|
+
self.all(:conditions => {:user_id => authority_id, :model_name => model_name})
|
57
|
+
when :self
|
58
|
+
self.all(:conditions => {:user_id => authority_id, :model_name => model_name})
|
59
|
+
else
|
60
|
+
[]
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Replaces the list with the data - only for the list found for the current authority.
|
67
|
+
# If the list is not found, it's created.
|
68
|
+
def self.update_list_for_current_authority(pref_name, data, model_name = nil)
|
69
|
+
pref = find_or_create_pref_to_read(pref_name)
|
70
|
+
pref.value = data.to_json
|
71
|
+
pref.model_name = model_name
|
72
|
+
pref.save!
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# If the <tt>model</tt> param is provided, then this preference will be assigned a parent preference
|
77
|
+
# that configures the attributes for that model. This way we can track all preferences related to a model.
|
78
|
+
def self.write_list(name, list, model = nil)
|
79
|
+
pref_to_store_the_list = self.pref_to_write(name)
|
80
|
+
pref_to_store_the_list.try(:update_attribute, :value, list.to_json)
|
81
|
+
|
82
|
+
# link this preference to the parent that contains default attributes for the same model
|
83
|
+
if model
|
84
|
+
model_level_attrs_pref = self.pref_to_read("#{model.tableize}_model_attrs")
|
85
|
+
model_level_attrs_pref.children << pref_to_store_the_list if model_level_attrs_pref && pref_to_store_the_list
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.read_list(name)
|
90
|
+
json_encoded_value = self.pref_to_read(name).try(:value)
|
91
|
+
ActiveSupport::JSON.decode(json_encoded_value).map(&:symbolize_keys) if json_encoded_value
|
92
|
+
end
|
93
|
+
|
94
|
+
# Read model-level attrs
|
95
|
+
# def self.read_attrs_for_model(model_name)
|
96
|
+
# read_list(model_name)
|
97
|
+
# # read_list("#{model.tableize}_model_attrs")
|
98
|
+
# end
|
99
|
+
|
100
|
+
# Write model-level attrs
|
101
|
+
# def self.write_attrs_for_model(model_name, data)
|
102
|
+
# # write_list("#{model_name.tableize}_model_attrs", data)
|
103
|
+
# write_list(model_name, data)
|
104
|
+
# end
|
105
|
+
|
106
|
+
# Options:
|
107
|
+
# :attr - attribute to propagate. If not specified, all attrs found in configuration for the model
|
108
|
+
# will be propagated.
|
109
|
+
def self.update_children_on_attr(model, options = {})
|
110
|
+
attr_name = options[:attr].try(:to_s)
|
111
|
+
|
112
|
+
parent_pref = pref_to_read("#{model.tableize}_model_attrs")
|
113
|
+
|
114
|
+
if parent_pref
|
115
|
+
parent_list = ActiveSupport::JSON.decode(parent_pref.value)
|
116
|
+
parent_pref.children.each do |ch|
|
117
|
+
child_list = ActiveSupport::JSON.decode(ch.value)
|
118
|
+
|
119
|
+
if attr_name
|
120
|
+
# propagate a certain attribute
|
121
|
+
propagate_attr(attr_name, parent_list, child_list)
|
122
|
+
else
|
123
|
+
# propagate all attributes found in parent
|
124
|
+
all_attrs = parent_list.first.try(:keys)
|
125
|
+
all_attrs && all_attrs.each{ |attr_name| propagate_attr(attr_name, parent_list, child_list) }
|
126
|
+
end
|
127
|
+
|
128
|
+
ch.update_attribute(:value, child_list.to_json)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# meta_attrs:
|
134
|
+
# {"city"=>{"included"=>true}, "building_number"=>{"default_value"=>100}}
|
135
|
+
def self.update_children(model, meta_attrs)
|
136
|
+
parent_pref = pref_to_read("#{model.tableize}_model_attrs")
|
137
|
+
|
138
|
+
|
139
|
+
if parent_pref
|
140
|
+
parent_pref.children.each do |ch|
|
141
|
+
child_list = ActiveSupport::JSON.decode(ch.value)
|
142
|
+
|
143
|
+
meta_attrs.each_pair do |k,v|
|
144
|
+
child_list.detect{ |child_attr| child_attr["name"] == k }.try(:merge!, v)
|
145
|
+
end
|
146
|
+
|
147
|
+
ch.update_attribute(:value, child_list.to_json)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def self.propagate_attr(attr_name, src_list, dest_list)
|
155
|
+
for src_field in src_list
|
156
|
+
dest_field = dest_list.detect{ |df| df["name"] == src_field["name"] }
|
157
|
+
dest_field[attr_name] = src_field[attr_name] if dest_field && src_field[attr_name]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Overwrite pref_to_read, pref_to_write methods, and find_all_for_widget if you want a different way of
|
162
|
+
# identifying the proper preference based on your own authorization strategy.
|
163
|
+
#
|
164
|
+
# The default strategy is:
|
165
|
+
# 1) if no masq_user or masq_role defined
|
166
|
+
# pref_to_read will search for the preference for user first, then for user's role
|
167
|
+
# pref_to_write will always find or create a preference for the current user (never for its role)
|
168
|
+
# 2) if masq_user or masq_role is defined
|
169
|
+
# pref_to_read and pref_to_write will always take the masquerade into account, e.g. reads/writes will go to
|
170
|
+
# the user/role specified
|
171
|
+
#
|
172
|
+
def self.pref_to_read(name)
|
173
|
+
name = name.to_s
|
174
|
+
session = Netzke::Base.session
|
175
|
+
cond = {:name => name}
|
176
|
+
|
177
|
+
if session[:masq_user]
|
178
|
+
# first, get the prefs for this user it they exist
|
179
|
+
res = self.find(:first, :conditions => cond.merge({:user_id => session[:masq_user]}))
|
180
|
+
# if it doesn't exist, get them for the user's role
|
181
|
+
user = User.find(session[:masq_user])
|
182
|
+
res ||= self.find(:first, :conditions => cond.merge({:role_id => user.role.id}))
|
183
|
+
# if it doesn't exist either, get them for the World (role_id = 0)
|
184
|
+
res ||= self.find(:first, :conditions => cond.merge({:role_id => 0}))
|
185
|
+
elsif session[:masq_role]
|
186
|
+
# first, get the prefs for this role
|
187
|
+
res = self.find(:first, :conditions => cond.merge({:role_id => session[:masq_role]}))
|
188
|
+
# if it doesn't exist, get them for the World (role_id = 0)
|
189
|
+
res ||= self.find(:first, :conditions => cond.merge({:role_id => 0}))
|
190
|
+
elsif session[:masq_world]
|
191
|
+
res = self.find(:first, :conditions => cond.merge({:role_id => 0}))
|
192
|
+
elsif session[:netzke_user_id]
|
193
|
+
user = User.find(session[:netzke_user_id])
|
194
|
+
# first, get the prefs for this user
|
195
|
+
res = self.find(:first, :conditions => cond.merge({:user_id => user.id}))
|
196
|
+
# if it doesn't exist, get them for the user's role
|
197
|
+
res ||= self.find(:first, :conditions => cond.merge({:role_id => user.role.id}))
|
198
|
+
# if it doesn't exist either, get them for the World (role_id = 0)
|
199
|
+
res ||= self.find(:first, :conditions => cond.merge({:role_id => 0}))
|
200
|
+
else
|
201
|
+
res = self.find(:first, :conditions => cond)
|
202
|
+
end
|
203
|
+
|
204
|
+
res
|
205
|
+
end
|
206
|
+
|
207
|
+
def self.find_or_create_pref_to_read(name)
|
208
|
+
name = name.to_s
|
209
|
+
attrs = {:name => name}
|
210
|
+
extend_attrs_for_current_authority(attrs)
|
211
|
+
self.first(:conditions => attrs) || self.new(attrs)
|
212
|
+
end
|
213
|
+
|
214
|
+
def self.extend_attrs_for_current_authority(hsh)
|
215
|
+
authority_level, authority_id = Netzke::Base.authority_level
|
216
|
+
case authority_level
|
217
|
+
when :world
|
218
|
+
hsh.merge!(:role_id => 0)
|
219
|
+
when :role
|
220
|
+
hsh.merge!(:role_id => authority_id)
|
221
|
+
when :user
|
222
|
+
hsh.merge!(:user_id => authority_id)
|
223
|
+
when :self
|
224
|
+
hsh.merge!(:user_id => authority_id)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.pref_to_write(name)
|
229
|
+
name = name.to_s
|
230
|
+
session = Netzke::Base.session
|
231
|
+
cond = {:name => name}
|
232
|
+
|
233
|
+
if session[:masq_user]
|
234
|
+
cond.merge!({:user_id => session[:masq_user]})
|
235
|
+
# first, try to find the preference for masq_user
|
236
|
+
res = self.find(:first, :conditions => cond)
|
237
|
+
# if it doesn't exist, create it
|
238
|
+
res ||= self.new(cond)
|
239
|
+
elsif session[:masq_role]
|
240
|
+
# first, delete all the corresponding preferences for the users that have this role
|
241
|
+
Role.find(session[:masq_role]).users.each do |u|
|
242
|
+
self.delete_all(cond.merge({:user_id => u.id}))
|
243
|
+
end
|
244
|
+
cond.merge!({:role_id => session[:masq_role]})
|
245
|
+
res = self.find(:first, :conditions => cond)
|
246
|
+
res ||= self.new(cond)
|
247
|
+
elsif session[:masq_world]
|
248
|
+
# first, delete all the corresponding preferences for all users and roles
|
249
|
+
self.delete_all(cond)
|
250
|
+
# then, create the new preference for the World (role_id = 0)
|
251
|
+
res = self.new(cond.merge(:role_id => 0))
|
252
|
+
elsif session[:netzke_user_id]
|
253
|
+
res = self.find(:first, :conditions => cond.merge({:user_id => session[:netzke_user_id]}))
|
254
|
+
res ||= self.new(cond.merge({:user_id => session[:netzke_user_id]}))
|
255
|
+
else
|
256
|
+
res = self.find(:first, :conditions => cond)
|
257
|
+
res ||= self.new(cond)
|
258
|
+
end
|
259
|
+
res
|
260
|
+
end
|
261
|
+
end
|