netzke-basepack 0.5.8 → 0.5.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|