social_stream-base 0.6.3 → 0.6.5
Sign up to get free protection for your applications and to get access to all the features.
- data/app/assets/stylesheets/base.css +1 -0
- data/app/assets/stylesheets/contacts.css +2 -1
- data/app/assets/stylesheets/fcbkComplete.css +1 -1
- data/app/assets/stylesheets/header.css +2 -2
- data/app/assets/stylesheets/messages.css +3 -3
- data/app/assets/stylesheets/{spheres.css → relation_customs.css} +0 -0
- data/app/controllers/contacts_controller.rb +9 -4
- data/app/controllers/messages_controller.rb +69 -68
- data/app/controllers/relation/customs_controller.rb +1 -11
- data/app/helpers/activities_helper.rb +1 -2
- data/app/helpers/notifications_helper.rb +3 -2
- data/app/helpers/permissions_helper.rb +5 -5
- data/app/helpers/subjects_helper.rb +9 -0
- data/app/helpers/toolbar_helper.rb +10 -89
- data/app/models/activity.rb +39 -15
- data/app/models/actor.rb +30 -71
- data/app/models/contact.rb +15 -0
- data/app/models/group.rb +1 -3
- data/app/models/permission.rb +9 -72
- data/app/models/profile.rb +7 -2
- data/app/models/relation/custom.rb +6 -33
- data/app/models/relation.rb +15 -39
- data/app/models/user.rb +5 -1
- data/app/views/activities/_new.html.erb +5 -2
- data/app/views/activities/_options.html.erb +2 -2
- data/app/views/activities/_walls.html.erb +1 -1
- data/app/views/contacts/_contact.html.erb +3 -3
- data/app/views/contacts/_form.html.erb +14 -20
- data/app/views/contacts/_index.html.erb +1 -1
- data/app/views/contacts/_suggestions.html.erb +1 -13
- data/app/views/contacts/destroy.js.erb +6 -0
- data/app/views/contacts/edit.html.erb +1 -1
- data/app/views/conversations/_conversation.html.erb +1 -1
- data/app/views/groups/_group.html.erb +1 -1
- data/app/views/groups/_index.html.erb +1 -1
- data/app/views/groups/show.html.erb +1 -1
- data/app/views/invitation_mailer/send_invitation.html.erb +17 -6
- data/app/views/invitation_mailer/send_invitation.text.erb +7 -2
- data/app/views/layouts/_representation.html.erb +10 -6
- data/app/views/layouts/_settings.html.erb +1 -1
- data/app/views/messages/_message.html.erb +1 -1
- data/app/views/messages/new.html.erb +1 -1
- data/app/views/notifications/_notification.html.erb +7 -3
- data/app/views/objects/_new.html.erb +0 -6
- data/app/views/profiles/edit.html.erb +5 -9
- data/app/views/relation/customs/_form.html.erb +2 -2
- data/app/views/{spheres → relation/customs}/_jquery.erb +0 -58
- data/app/views/relation/customs/_list.html.erb +9 -26
- data/app/views/relation/customs/create.js.erb +2 -3
- data/app/views/relation/customs/index.html.erb +67 -0
- data/app/views/users/_index.html.erb +1 -1
- data/app/views/users/show.html.erb +1 -1
- data/config/locales/en.yml +16 -41
- data/config/routes.rb +1 -6
- data/db/migrate/20110705103202_empty_ties_count.rb +4 -0
- data/db/migrate/20110712090343_remove_spheres.rb +30 -0
- data/db/migrate/20110712142140_remove_permission_function.rb +26 -0
- data/lib/generators/social_stream/base/install_generator.rb +4 -0
- data/lib/generators/social_stream/base/templates/mailboxer_custom.rb +13 -0
- data/lib/generators/social_stream/base/templates/navigation.rb +4 -0
- data/lib/generators/social_stream/base/templates/relations.yml +14 -20
- data/lib/social_stream/ability.rb +1 -2
- data/lib/social_stream/base/version.rb +1 -1
- data/lib/social_stream/migration_finder.rb +19 -0
- data/lib/social_stream/toolbar_config.rb +113 -0
- data/lib/social_stream-base.rb +1 -0
- data/lib/tasks/db/populate.rake +1 -1
- data/social_stream-base.gemspec +4 -2
- data/spec/controllers/contacts_controller_spec.rb +1 -1
- data/spec/controllers/permissions_controller_spec.rb +1 -2
- data/spec/controllers/posts_controller_spec.rb +2 -2
- data/spec/controllers/relation_customs_controller_spec.rb +69 -62
- data/spec/dummy/config/database.yml +7 -0
- data/spec/dummy/config/relations.yml +14 -20
- data/spec/factories/relation_custom.rb +1 -1
- data/spec/factories/tie.rb +4 -0
- data/spec/models/activity_spec.rb +11 -2
- data/spec/models/user_spec.rb +68 -9
- data/spec/spec_helper.rb +0 -3
- data/spec/support/db.rb +9 -0
- data/spec/support/migrations.rb +3 -12
- metadata +19 -25
- data/app/controllers/spheres_controller.rb +0 -12
- data/app/models/sphere.rb +0 -9
- data/app/views/relation/customs/_index.html.erb +0 -28
- data/app/views/relation/customs/index.js.erb +0 -2
- data/app/views/spheres/_form.html.erb +0 -28
- data/app/views/spheres/_list.html.erb +0 -19
- data/app/views/spheres/create.js.erb +0 -20
- data/app/views/spheres/index.html.erb +0 -74
- data/spec/controllers/spheres_controller_spec.rb +0 -116
- data/spec/factories/sphere.rb +0 -5
- data/spec/models/relation_custom_spec.rb +0 -14
@@ -0,0 +1,19 @@
|
|
1
|
+
module SocialStream
|
2
|
+
# Searches for migrations in a gem and requires them.
|
3
|
+
# Example:
|
4
|
+
#
|
5
|
+
# MigrationFinder.new 'acts-as-taggable-on',
|
6
|
+
# ["generators", "acts_as_taggable_on", "migration", "templates", "active_record", "migration"]
|
7
|
+
# ActsAsTaggableOnMigration.up
|
8
|
+
class MigrationFinder
|
9
|
+
def initialize gem, path
|
10
|
+
finder = Gem::GemPathSearcher.new
|
11
|
+
taggable_spec = finder.find(gem)
|
12
|
+
taggable_migration = finder.matching_files(taggable_spec,
|
13
|
+
File.join(*path)).first
|
14
|
+
|
15
|
+
require taggable_migration
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module SocialStream
|
2
|
+
module ToolbarConfig
|
3
|
+
#Prints the default home toolbar menu
|
4
|
+
def default_home_toolbar_menu
|
5
|
+
items = Array.new
|
6
|
+
#Notifications
|
7
|
+
items << {:key => :notifications,
|
8
|
+
:name => image_tag("btn/btn_notification.png")+t('notification.other')+' ('+ current_subject.mailbox.notifications.not_trashed.unread.count.to_s+')',
|
9
|
+
:url => notifications_path,
|
10
|
+
:options => {:link => {:id => "notifications_menu"}}}
|
11
|
+
|
12
|
+
#Messages
|
13
|
+
items << {:key => :messages,
|
14
|
+
:name => image_tag("btn/new.png")+t('message.other')+' (' + current_subject.mailbox.inbox(:unread => true).count.to_s + ')',
|
15
|
+
:url => "#",
|
16
|
+
:options => {:link => {:id => "messages_menu"}},
|
17
|
+
:items => [
|
18
|
+
{:key => :message_new, :name => image_tag("btn/message_new.png")+ t('message.new'), :url => new_message_path},
|
19
|
+
{:key => :message_inbox, :name => image_tag("btn/message_inbox.png")+t('message.inbox')+' (' + current_subject.mailbox.inbox(:unread => true).count.to_s + ')',
|
20
|
+
:url => conversations_path, :options => {:link =>{:remote=> true}}},
|
21
|
+
{:key => :message_sentbox, :name => image_tag("btn/message_sentbox.png")+t('message.sentbox'), :url => conversations_path(:box => :sentbox), :remote=> true},
|
22
|
+
{:key => :message_trash, :name => image_tag("btn/message_trash.png")+t('message.trash'), :url => conversations_path(:box => :trash)}
|
23
|
+
]}
|
24
|
+
|
25
|
+
#Documents if present
|
26
|
+
if SocialStream.activity_forms.include? :document
|
27
|
+
items << {:key => :resources,
|
28
|
+
:name => image_tag("btn/btn_resource.png",:class =>"menu_icon")+t('resource.mine'),
|
29
|
+
:url => "#",
|
30
|
+
:options => {:link => {:id => "resources_menu"}},
|
31
|
+
:items => [
|
32
|
+
{:key => :resources_documents,:name => image_tag("btn/btn_documents.png")+t('document.title'),:url => documents_path},
|
33
|
+
{:key => :resources_pictures,:name => image_tag("btn/btn_gallery.png")+t('picture.title'),:url => pictures_path},
|
34
|
+
{:key => :resources_videos,:name => image_tag("btn/btn_video.png")+t('video.title'),:url => videos_path},
|
35
|
+
{:key => :resources_audios,:name => image_tag("btn/btn_audio.png")+t('audio.title'),:url => audios_path}
|
36
|
+
]}
|
37
|
+
end
|
38
|
+
|
39
|
+
#Contacts
|
40
|
+
relation_items = [{:key => :invitations, :name => image_tag("btn/btn_invitation.png")+t('invitation.other'), :url => new_invitation_path}]
|
41
|
+
current_subject.relation_customs.sort.each do |r|
|
42
|
+
relation_items << {:key => r.name + "_menu",
|
43
|
+
:name => image_tag("btn/btn_friend.png") + r.name,
|
44
|
+
:url => contacts_path(:relation => r.id)}
|
45
|
+
end
|
46
|
+
items << {:key => :contacts,
|
47
|
+
:name => image_tag("btn/btn_friend.png")+t('contact.other'),
|
48
|
+
:url => "#",
|
49
|
+
:options => {:link => {:id => "contacts_menu"}},
|
50
|
+
:items => relation_items}
|
51
|
+
|
52
|
+
#Subjects
|
53
|
+
items << {:key => :groups,
|
54
|
+
:name => image_tag("btn/btn_group.png")+t('group.other'),
|
55
|
+
:url => "#",
|
56
|
+
:options => {:link => {:id => "groups_menu"}},
|
57
|
+
:items => [{:key => :new_group ,:name => image_tag("btn/btn_group.png")+t('group.new.action'),:url => new_group_path('group' => { '_founder' => current_subject.slug })}]
|
58
|
+
}
|
59
|
+
|
60
|
+
render_items items
|
61
|
+
end
|
62
|
+
|
63
|
+
#Prints the default profile toolbar menu
|
64
|
+
def default_profile_toolbar_menu(subject = current_subject)
|
65
|
+
items = Array.new
|
66
|
+
#Information button
|
67
|
+
items << {:key => :subject_info,
|
68
|
+
:name => image_tag("btn/btn_edit.png")+t('menu.information'),
|
69
|
+
:url => [subject, :profile]
|
70
|
+
}
|
71
|
+
|
72
|
+
if subject!=current_subject
|
73
|
+
#Like button
|
74
|
+
items << {:key => :like_button,
|
75
|
+
:name => link_like_params(subject)[0],
|
76
|
+
:url => link_like_params(subject)[1],
|
77
|
+
:options => {:link => link_like_params(subject)[2]}}
|
78
|
+
|
79
|
+
if user_signed_in?
|
80
|
+
#Relation button
|
81
|
+
items << {:key => :subject_relation,
|
82
|
+
:name => image_tag("btn/btn_friend.png") + contact_status(subject),
|
83
|
+
:url => edit_contact_path(current_subject.contact_to!(subject))
|
84
|
+
}
|
85
|
+
#Send message button
|
86
|
+
items << {:key => :send_message,
|
87
|
+
:name => image_tag("btn/btn_send.png")+t('message.send'),
|
88
|
+
:url => new_message_path(:receiver => subject.slug)
|
89
|
+
}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
#Documents if present
|
93
|
+
if SocialStream.activity_forms.include? :document
|
94
|
+
if subject == current_subject
|
95
|
+
resources_label = t('resource.mine')
|
96
|
+
else
|
97
|
+
resources_label = t('resource.title')
|
98
|
+
end
|
99
|
+
items << {:key => :resources,
|
100
|
+
:name => image_tag("btn/btn_resource.png",:class =>"menu_icon")+resources_label,
|
101
|
+
:url => "#",
|
102
|
+
:options => {:link => {:id => "resources_menu"}},
|
103
|
+
:items => [
|
104
|
+
{:key => :resources_documents,:name => image_tag("btn/btn_documents.png")+t('document.title'),:url => documents_path},
|
105
|
+
{:key => :resources_pictures,:name => image_tag("btn/btn_gallery.png")+t('picture.title'),:url => pictures_path},
|
106
|
+
{:key => :resources_videos,:name => image_tag("btn/btn_video.png")+t('video.title'),:url => videos_path},
|
107
|
+
{:key => :resources_audios,:name => image_tag("btn/btn_audio.png")+t('audio.title'),:url => audios_path}
|
108
|
+
]}
|
109
|
+
end
|
110
|
+
render_items items
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/social_stream-base.rb
CHANGED
@@ -40,6 +40,7 @@ module SocialStream
|
|
40
40
|
autoload :Populate, 'social_stream/populate'
|
41
41
|
autoload :Relations, 'social_stream/relations'
|
42
42
|
autoload :TestHelpers, 'social_stream/test_helpers'
|
43
|
+
autoload :ToolbarConfig, 'social_stream/toolbar_config'
|
43
44
|
|
44
45
|
module Controllers
|
45
46
|
autoload :Helpers, 'social_stream/controllers/helpers'
|
data/lib/tasks/db/populate.rake
CHANGED
@@ -118,7 +118,7 @@ namespace :db do
|
|
118
118
|
|
119
119
|
p = Post.create :text =>
|
120
120
|
"This post should be for #{ t.relation.name } of #{ t.sender.name }.\n#{ Forgery::LoremIpsum.paragraph(:random => true) }",
|
121
|
-
:created_at => Time.at(rand(updated)),
|
121
|
+
:created_at => Time.at(rand(updated.to_i)),
|
122
122
|
:updated_at => updated,
|
123
123
|
:_contact_id => t.contact_id,
|
124
124
|
:_relation_ids => Array(t.relation_id)
|
data/social_stream-base.gemspec
CHANGED
@@ -35,7 +35,7 @@ Gem::Specification.new do |s|
|
|
35
35
|
# OAuth provider
|
36
36
|
s.add_runtime_dependency('oauth-plugin','~> 0.4.0.pre1')
|
37
37
|
# Messages
|
38
|
-
s.add_runtime_dependency('mailboxer','~> 0.2
|
38
|
+
s.add_runtime_dependency('mailboxer','~> 0.3.2')
|
39
39
|
# Avatar manipulation
|
40
40
|
s.add_runtime_dependency('rmagick','~> 2.13.1')
|
41
41
|
# Tagging
|
@@ -56,7 +56,9 @@ Gem::Specification.new do |s|
|
|
56
56
|
s.add_development_dependency('sqlite3-ruby')
|
57
57
|
# Debugging
|
58
58
|
if RUBY_VERSION < '1.9'
|
59
|
-
s.add_development_dependency('ruby-debug'
|
59
|
+
s.add_development_dependency('ruby-debug')
|
60
|
+
else
|
61
|
+
s.add_development_dependency('ruby-debug19')
|
60
62
|
end
|
61
63
|
# Specs
|
62
64
|
s.add_development_dependency('rspec-rails', '~> 2.6.1')
|
@@ -63,7 +63,7 @@ describe ContactsController do
|
|
63
63
|
contact = @user.contact_to!(group)
|
64
64
|
# Initialize inverse contact
|
65
65
|
contact.inverse!
|
66
|
-
relations = [ @user.relation_custom('friend'), @user.relation_custom('
|
66
|
+
relations = [ @user.relation_custom('friend'), @user.relation_custom('colleague') ]
|
67
67
|
|
68
68
|
|
69
69
|
put :update, :id => contact.id,
|
@@ -14,8 +14,7 @@ describe PermissionsController do
|
|
14
14
|
|
15
15
|
context "with an existing relation" do
|
16
16
|
before do
|
17
|
-
@
|
18
|
-
@relation = Factory(:relation_custom, :sphere_id => @sphere.id)
|
17
|
+
@relation = Factory(:relation_custom, :actor_id => @user.actor_id)
|
19
18
|
end
|
20
19
|
|
21
20
|
it "should render index" do
|
@@ -76,10 +76,10 @@ describe PostsController do
|
|
76
76
|
@group = Factory(:member, :contact => Factory(:group_contact, :receiver => @user.actor)).sender_subject
|
77
77
|
end
|
78
78
|
|
79
|
-
describe "with
|
79
|
+
describe "with member relation" do
|
80
80
|
before do
|
81
81
|
contact = @user.contact_to!(@group)
|
82
|
-
relation = @group.
|
82
|
+
relation = @group.relation_custom('member')
|
83
83
|
|
84
84
|
model_assigned_to contact, relation
|
85
85
|
@current_model = Factory(:post, :_contact_id => contact.id, :_relation_ids => Array(relation.id))
|
@@ -1,103 +1,112 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
3
|
describe Relation::CustomsController do
|
4
|
-
include SocialStream::TestHelpers::Controllers
|
5
|
-
|
6
4
|
render_views
|
7
5
|
|
8
|
-
describe "when
|
9
|
-
|
10
|
-
|
6
|
+
describe "when Anonymous" do
|
7
|
+
context "faking a new relation" do
|
8
|
+
it "should not create" do
|
9
|
+
post :create, :custom => Factory.attributes_for(:relation_custom)
|
11
10
|
|
12
|
-
|
11
|
+
response.should redirect_to(:new_user_session)
|
12
|
+
end
|
13
13
|
end
|
14
14
|
|
15
|
-
context "
|
15
|
+
context "an existing relation" do
|
16
16
|
before do
|
17
|
-
@
|
17
|
+
@relation = Factory(:relation_custom)
|
18
18
|
end
|
19
19
|
|
20
|
-
it "should
|
21
|
-
|
20
|
+
it "should not update" do
|
21
|
+
put :update, :id => @relation.to_param, :custom => { :name => 'Testing' }
|
22
22
|
|
23
|
-
|
23
|
+
assigns(:custom).should be_blank
|
24
|
+
response.should redirect_to(:new_user_session)
|
24
25
|
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
it "should not destroy" do
|
28
|
+
count = Relation.count
|
29
|
+
begin
|
30
|
+
delete :destroy, :id => @relation.to_param
|
31
|
+
rescue CanCan::AccessDenied
|
32
|
+
end
|
29
33
|
|
30
|
-
|
34
|
+
relation = assigns(:custom)
|
31
35
|
|
32
|
-
|
36
|
+
Relation.count.should eq(count)
|
37
|
+
end
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
response.should be_success
|
37
|
-
end
|
39
|
+
end
|
40
|
+
end
|
38
41
|
|
39
|
-
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
describe "when authenticated" do
|
44
|
+
before do
|
45
|
+
@user = Factory(:user)
|
46
|
+
|
47
|
+
sign_in @user
|
48
|
+
end
|
46
49
|
|
47
|
-
|
48
|
-
|
50
|
+
it "should render index" do
|
51
|
+
get :index
|
49
52
|
|
50
|
-
|
53
|
+
response.should be_success
|
54
|
+
end
|
51
55
|
|
52
|
-
|
56
|
+
context "a new own relation" do
|
57
|
+
it "should be created" do
|
58
|
+
count = Relation::Custom.count
|
53
59
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
60
|
+
post :create, :custom => { :name => "Test create", :actor_id => @user.actor_id }, :format => 'js'
|
61
|
+
|
62
|
+
relation = assigns(:custom)
|
58
63
|
|
59
|
-
|
60
|
-
|
64
|
+
Relation::Custom.count.should eq(count + 1)
|
65
|
+
relation.should be_valid
|
66
|
+
response.should be_success
|
67
|
+
end
|
68
|
+
end
|
61
69
|
|
62
|
-
|
70
|
+
context "a new fake relation" do
|
71
|
+
it "should not be created" do
|
72
|
+
count = Relation.count
|
73
|
+
begin
|
74
|
+
post :create, :custom => Factory.attributes_for(:relation_custom)
|
63
75
|
|
64
|
-
|
76
|
+
assert false
|
77
|
+
rescue CanCan::AccessDenied
|
78
|
+
assigns(:custom).should be_new_record
|
65
79
|
|
66
|
-
Relation
|
80
|
+
Relation.count.should eq(count)
|
67
81
|
end
|
68
82
|
end
|
69
|
-
|
70
83
|
end
|
71
84
|
|
72
|
-
context "
|
85
|
+
context "a existing own relation" do
|
73
86
|
before do
|
74
|
-
@
|
87
|
+
@relation = Factory(:relation_custom, :actor => @user.actor)
|
75
88
|
end
|
76
89
|
|
77
|
-
it "should
|
78
|
-
|
79
|
-
get :index, :sphere_id => @sphere.id, :format => "js"
|
90
|
+
it "should allow updating" do
|
91
|
+
attrs = { :name => "Updating own" }
|
80
92
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
93
|
+
put :update, :id => @relation.to_param, :relation_custom => attrs, :format => 'js'
|
94
|
+
|
95
|
+
relation = assigns(:custom)
|
96
|
+
|
97
|
+
# relation.should_receive(:update_attributes).with(attrs)
|
98
|
+
relation.should be_valid
|
99
|
+
response.should be_success
|
86
100
|
end
|
87
101
|
|
88
|
-
|
89
|
-
|
90
|
-
count = Relation::Custom.count
|
102
|
+
it "should allow destroying" do
|
103
|
+
pending "Delete relations"
|
91
104
|
|
92
|
-
|
93
|
-
post :create, :relation_custom => { :name => "Test create", :sphere_id => @sphere.id }, :format => 'js'
|
105
|
+
count = Relation::Custom.count
|
94
106
|
|
95
|
-
|
96
|
-
rescue CanCan::AccessDenied
|
107
|
+
delete :destroy, :id => @relation.to_param, :format => :js
|
97
108
|
|
98
|
-
|
99
|
-
end
|
100
|
-
end
|
109
|
+
Relation::Custom.count.should eq(count - 1)
|
101
110
|
end
|
102
111
|
end
|
103
112
|
|
@@ -126,8 +135,6 @@ describe Relation::CustomsController do
|
|
126
135
|
end
|
127
136
|
end
|
128
137
|
end
|
129
|
-
|
130
|
-
|
131
138
|
end
|
132
139
|
end
|
133
140
|
|
@@ -11,35 +11,29 @@ user:
|
|
11
11
|
friend:
|
12
12
|
name: friend
|
13
13
|
permissions:
|
14
|
-
- [ follow ]
|
15
|
-
- [ create, activity
|
16
|
-
- [ read, activity
|
17
|
-
sphere: personal
|
14
|
+
- [ follow ]
|
15
|
+
- [ create, activity ]
|
16
|
+
- [ read, activity ]
|
18
17
|
acquaintance:
|
19
18
|
name: acquaintance
|
20
|
-
parent: friend # friend relation is stronger than acquaintance
|
21
19
|
permissions:
|
22
|
-
- [ read, activity
|
23
|
-
|
24
|
-
|
25
|
-
name: partner
|
20
|
+
- [ read, activity ]
|
21
|
+
colleague:
|
22
|
+
name: colleague
|
26
23
|
permissions:
|
27
|
-
- [
|
28
|
-
|
24
|
+
- [ follow ]
|
25
|
+
- [ create, activity ]
|
26
|
+
- [ read, activity ]
|
29
27
|
|
30
28
|
group:
|
31
29
|
member:
|
32
30
|
name: member
|
33
31
|
permissions:
|
34
|
-
- [ represent ]
|
35
|
-
- [ create, activity
|
36
|
-
- [ read, activity
|
37
|
-
- [ read, tie
|
38
|
-
sphere: organization
|
32
|
+
- [ represent ]
|
33
|
+
- [ create, activity ]
|
34
|
+
- [ read, activity ]
|
35
|
+
- [ read, tie ]
|
39
36
|
partner:
|
40
37
|
name: partner
|
41
|
-
parent: member # member is stronger than partner
|
42
38
|
permissions:
|
43
|
-
- [ read, activity
|
44
|
-
sphere: external_relations
|
45
|
-
|
39
|
+
- [ read, activity ]
|